Map Debugging — Common Issues and Solutions
Why Debugging Matters
Map applications have many moving parts — map tiles, markers, popups, API calls, authentication, and spatial queries. When something breaks, knowing where to look saves hours of frustration.
Why this matters for your career:
- Debugging is the most common activity in software development
- Map-specific issues (tiles, projections, coordinates) require specialized knowledge
- Systematic debugging separates junior from senior developers
- Understanding common failure modes helps you prevent them in the first place
Common Map Issues and Solutions
1. Blank Map (No Tiles)
Symptoms: Map container is visible but shows only a gray background.
Causes and fixes:
| Cause | Fix |
|-------|-----|
| No tile layer added | Add L.tileLayer(...).addTo(map) |
| Wrong tile URL | Check the tile server URL is correct and accessible |
| API key missing | Some tile providers require an API key |
| HTTPS mixed content | If your site is HTTPS, tile URL must also be HTTPS |
| CORS blocked | Tile server must allow CORS or use a proxy |
| Ad blocker | Some ad blockers block tile servers — test in incognito mode |
// Fix: Check tile layer configuration
const tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
});
// Verify the tile URL works by testing in browser:
// https://a.tile.openstreetmap.org/10/1024/768.png
tileLayer.on('tileerror', function(error) {
console.error('Tile failed to load:', error);
});
tileLayer.addTo(map);
2. Markers Not Showing
Symptoms: Markers are not visible on the map.
| Cause | Fix |
|-------|-----|
| Markers added before map initialization | Create map first, then add markers |
| Wrong coordinates (lat/lng swapped) | Leaflet uses [lat, lng] — NOT [lng, lat] |
| Markers outside map bounds | Check marker coordinates are within the viewable area |
| Marker layer not added to map | Call marker.addTo(map) |
| Cluster group not added to map | Call mcg.addTo(map) for cluster groups |
| CSS overflow hidden | Check the map container CSS |
// Fix: Proper marker creation
const marker = L.marker([25.033, 121.565]); // [latitude, longitude]
marker.addTo(map);
// Verify coordinates
console.log('Marker position:', marker.getLatLng());
// Check map bounds contain the marker
const bounds = map.getBounds();
console.log('Marker in bounds:', bounds.contains(marker.getLatLng()));
3. Wrong Coordinates
Symptoms: Markers appear in unexpected locations (e.g., in the ocean).
| Cause | Fix |
|-------|-----|
| Lat/lng swapped | In Leaflet, it's [lat, lng]. In APIs, JSON often uses {lng, lat} |
| Wrong coordinate system | GPS uses WGS84 (EPSG:4326). Some data uses other SRIDs |
| Sign error | Taiwan is north of equator (lat > 0) and east of Greenwich (lng > 0) |
| Copy-paste from wrong source | Verify coordinates from a reliable source |
// Correct: [latitude, longitude]
// Taiwan example: [25.033, 121.565]
function parseCoordinates(input) {
const [lat, lng] = input.split(',').map(Number);
if (isNaN(lat) || isNaN(lng)) {
throw new Error(`Invalid coordinates: ${input}`);
}
if (lat < -90 || lat > 90) {
throw new Error(`Latitude must be between -90 and 90: ${lat}`);
}
if (lng < -180 || lng > 180) {
throw new Error(`Longitude must be between -180 and 180: ${lng}`);
}
return { lat, lng };
}
4. CORS Errors
Symptoms: Browser console shows CORS errors when fetching data.
| Cause | Fix | |-------|-----| | API and frontend on different origins | Enable CORS on the API server | | Supabase requests blocked | Allow the frontend origin in Supabase settings | | Tile server doesn't support CORS | Use a CORS proxy or switch tile providers |
// Supabase CORS configuration (in Supabase dashboard)
// Settings → API → CORS → Add your frontend domain
// e.g., https://mycampingmap.vercel.app
5. Performance Issues
Symptoms: Map is slow, laggy, or crashes.
| Cause | Fix | |-------|-----| | Too many markers without clustering | Add marker clustering | | No spatial index on database | Add GIST index on location column | | Loading all data at once | Load only visible bounds | | Large tile set at low zoom | Limit min/max zoom levels | | Too many popups open simultaneously | Only show one popup at a time | | Memory leak from repeated marker creation | Reuse markers, clear old ones first |
// Performance checklist
// ✅ Marker clustering installed
// ✅ Spatial index on database
// ✅ Bounds-based loading
// ✅ Zoom level limits (minZoom: 6, maxZoom: 18)
// ✅ Single popup at a time
// ✅ Markers cleared when out of bounds
// ✅ Chunked loading for large datasets
// ✅ Debounced API calls on map movement
Debugging Tools
| Tool | Purpose |
|------|---------|
| Browser DevTools Console | See JavaScript errors and warnings |
| Network tab | Check API calls, tile loading, response times |
| map.on('tileerror', ...) | Detect failed tile loads |
| Leaflet debug plugin | Visualize layers and bounds |
| PostGIS EXPLAIN ANALYZE | Debug slow spatial queries |
| Supabase Dashboard | Query logs, error rates, API calls |
| Lighthouse | Performance audit and suggestions |
| GeoJSON.io | Validate GeoJSON data |
Debugging Leaflet Events
// Log all map events
const events = ['click', 'dblclick', 'moveend', 'zoomend', 'dragstart', 'dragend'];
events.forEach(event => {
map.on(event, (e) => console.log(`Event: ${event}`, e));
});
// Log tile loading events
map.on('load', () => console.log('Map fully loaded'));
map.on('tileload', (e) => console.log('Tile loaded:', e.url));
map.on('tileerror', (e) => console.error('Tile error:', e.url));
// Log layer events
map.on('layeradd', (e) => console.log('Layer added:', e.layer));
map.on('layerremove', (e) => console.log('Layer removed:', e.layer));
Debugging Spatial Queries
-- Check if PostGIS is using the spatial index
EXPLAIN ANALYZE
SELECT * FROM campsites
WHERE location && ST_MakeEnvelope(121.0, 24.5, 122.0, 25.5, 4326);
-- Look for "Index Scan" in the output — not "Seq Scan"
-- If you see "Seq Scan", your GIST index is missing or not being used
-- Verify the index exists
SELECT indexname, indexdef FROM pg_indexes
WHERE tablename = 'campsites';
Common Supabase Issues
| Issue | Solution | |-------|----------| | Row Level Security (RLS) blocks queries | Enable RLS with proper policies or disable for development | | CORS errors from Supabase | Add your domain in Supabase CORS settings | | Rate limiting (429) | Add client-side caching, reduce request frequency | | Large response payload | Add pagination, select only needed columns | | WebSocket connection drops | Check Supabase status page, reconnect on disconnect |
Browser-Specific Issues
| Browser | Known Issue | Workaround |
|---------|-------------|------------|
| Safari iOS | Geolocation requires HTTPS | Use HTTPS or development fallback |
| Chrome | Mixed content warnings for HTTP tiles | Use HTTPS tile URLs |
| Firefox | CORS strict mode | Ensure proper CORS headers |
| Mobile browsers | touch-action: manipulation needed | Add CSS rule for Leaflet container |
/* Fix mobile scroll interference */
.leaflet-container {
touch-action: manipulation;
}
Debugging Checklist
- ✅ Check browser console for errors
- ✅ Verify map container has a defined height (CSS)
- ✅ Confirm tile layer URL is correct and accessible
- ✅ Check coordinates are in [lat, lng] order
- ✅ Verify markers are within the initial map bounds
- ✅ Check for CORS errors in API calls
- ✅ Monitor network tab for failed requests
- ✅ Test in incognito/private mode (eliminates extension interference)
- ✅ Check database spatial index exists
- ✅ Verify Supabase RLS policies if queries return empty
Summary
Debugging map applications requires a systematic approach — check the console first, verify coordinates, inspect network requests, and test without extensions. Most issues fall into a few categories: wrong coordinates, missing layers, CORS errors, or performance problems.
Key takeaways:
- Always use
[latitude, longitude]order in Leaflet - Check tile URLs are HTTPS (avoid mixed content)
- Verify map container has explicit height in CSS
- Use the browser Network tab to inspect failed requests
- Add marker clustering before performance becomes an issue
- Check spatial indexes for slow database queries
- Test in incognito mode to rule out extension interference
- Use EXPLAIN ANALYZE to verify index usage
- Add tileerror event listener to detect failed tiles
- Use
touch-action: manipulationCSS for mobile Leaflet
What's Next: Search & Autocomplete
The next chapter covers search and autocomplete — helping users find campsites by name, location, or amenity with an efficient search interface.