1506 lines
34 KiB
Markdown
1506 lines
34 KiB
Markdown
# Geocoding and Map Issues
|
|
|
|
This guide covers geocoding, map display, and location-related problems in Changemaker Lite V2.
|
|
|
|
## Overview
|
|
|
|
### Geocoding System
|
|
|
|
Changemaker Lite V2 uses **multi-provider geocoding** with automatic fallback:
|
|
|
|
1. **Google Geocoding API** - Most accurate, requires API key
|
|
2. **Mapbox Geocoding API** - Good quality, requires API key
|
|
3. **Nominatim (OpenStreetMap)** - Free, no key required
|
|
4. **ArcGIS Geocoding Service** - Good for North America
|
|
5. **Photon (OpenStreetMap)** - Free alternative
|
|
6. **HERE Geocoding API** - Paid option
|
|
|
|
### Geocoding Queue
|
|
|
|
- **BullMQ queue** - Async geocoding for bulk imports
|
|
- **Rate limiting** - Respects provider rate limits
|
|
- **Retry logic** - Auto-retry failed geocodes
|
|
- **Priority** - Manual geocodes prioritized over bulk
|
|
|
|
### Map Display
|
|
|
|
- **Leaflet.js** - Open-source map library
|
|
- **OpenStreetMap tiles** - Free map tiles
|
|
- **Circle markers** - Color-coded by cut assignment
|
|
- **Polygon overlays** - Cut boundaries
|
|
- **Geolocate** - Find user's current location
|
|
|
|
---
|
|
|
|
## Geocoding Failures
|
|
|
|
### Address Not Found
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Location shows `null` latitude/longitude after geocoding attempt.
|
|
|
|
API logs:
|
|
```
|
|
WARN Geocoding failed for address: "123 Fake St, Nowhere": No results from any provider
|
|
```
|
|
|
|
#### Common Causes
|
|
|
|
1. **Invalid address** - Address doesn't exist
|
|
2. **Typo** - Misspelled street/city/postal code
|
|
3. **Incomplete address** - Missing city or postal code
|
|
4. **Wrong country** - Address in different country
|
|
5. **Rural address** - Not in geocoding databases
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Verify address format**
|
|
|
|
```bash
|
|
# Good address format (Canadian):
|
|
123 Main Street, Toronto, ON M5H 2N2
|
|
|
|
# Good address format (US):
|
|
123 Main Street, New York, NY 10001
|
|
|
|
# Bad formats:
|
|
123 Main # Missing city/postal
|
|
Main Street # Missing number
|
|
Toronto # Too vague
|
|
```
|
|
|
|
**Solution 2: Test address manually**
|
|
|
|
```bash
|
|
# Test via Nominatim (no API key needed)
|
|
curl "https://nominatim.openstreetmap.org/search?q=123+Main+Street,Toronto,ON&format=json"
|
|
|
|
# Should return array with results
|
|
# If empty, address not found
|
|
```
|
|
|
|
**Solution 3: Try alternative formats**
|
|
|
|
```bash
|
|
# If "123 Main Street, Toronto ON M5H 2N2" fails, try:
|
|
# - "123 Main St, Toronto ON M5H2N2" (no space in postal)
|
|
# - "123 Main Street, Toronto Ontario M5H 2N2" (full province)
|
|
# - "123 Main Street, M5H 2N2" (postal code only)
|
|
# - "M5H 2N2" (postal code geocoding)
|
|
```
|
|
|
|
**Solution 4: Check geocoding logs**
|
|
|
|
```bash
|
|
# View detailed geocoding attempts
|
|
docker compose logs api | grep "Geocoding\|geocode"
|
|
|
|
# Shows:
|
|
# Trying provider: google
|
|
# Google geocoding failed: Invalid request
|
|
# Trying provider: nominatim
|
|
# Nominatim geocoding succeeded
|
|
```
|
|
|
|
**Solution 5: Manually set coordinates**
|
|
|
|
In admin UI (LocationsPage):
|
|
|
|
1. Find location in table
|
|
2. Click Edit
|
|
3. Manually enter lat/lng (from Google Maps)
|
|
4. Save
|
|
|
|
Or via SQL:
|
|
|
|
```bash
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "UPDATE \"Location\" SET latitude = 43.65, longitude = -79.38
|
|
WHERE address = '123 Main Street';"
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Address validation** - Validate format before saving
|
|
- **Postal code lookup** - Use postal code if full address fails
|
|
- **Manual review** - Flag failed geocodes for manual review
|
|
- **Alternative sources** - Try multiple address formats
|
|
|
|
---
|
|
|
|
### All Providers Failed
|
|
|
|
**Severity:** 🟠 High
|
|
|
|
#### Symptoms
|
|
|
|
```
|
|
ERROR Geocoding failed: All providers failed for address: "123 Main St"
|
|
```
|
|
|
|
All 6 geocoding providers returned no results or errors.
|
|
|
|
#### Common Causes
|
|
|
|
1. **Network issue** - Can't reach external APIs
|
|
2. **Rate limits** - All providers rate limited
|
|
3. **Invalid API keys** - Google/Mapbox keys invalid
|
|
4. **Bad address** - Address truly doesn't exist
|
|
5. **Provider outages** - Services down
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Check network connectivity**
|
|
|
|
```bash
|
|
# Test DNS resolution
|
|
docker compose exec api ping -c 3 nominatim.openstreetmap.org
|
|
|
|
# Test HTTPS connection
|
|
docker compose exec api curl -I https://nominatim.openstreetmap.org
|
|
|
|
# If fails, network issue
|
|
```
|
|
|
|
**Solution 2: Test each provider manually**
|
|
|
|
```bash
|
|
# Nominatim (free, no key)
|
|
curl "https://nominatim.openstreetmap.org/search?q=123+Main+Street,Toronto&format=json"
|
|
|
|
# Google (requires GOOGLE_GEOCODING_API_KEY)
|
|
curl "https://maps.googleapis.com/maps/api/geocode/json?address=123+Main+Street,Toronto&key=YOUR_KEY"
|
|
|
|
# Mapbox (requires MAPBOX_API_KEY)
|
|
curl "https://api.mapbox.com/geocoding/v5/mapbox.places/123+Main+Street,Toronto.json?access_token=YOUR_KEY"
|
|
```
|
|
|
|
**Solution 3: Check API keys**
|
|
|
|
```bash
|
|
# Verify API keys in .env
|
|
cat .env | grep -E "GOOGLE_GEOCODING_API_KEY|MAPBOX_API_KEY|HERE_API_KEY"
|
|
|
|
# Should show non-empty values
|
|
# If empty, providers requiring keys won't work
|
|
```
|
|
|
|
**Solution 4: Check rate limits**
|
|
|
|
```bash
|
|
# View geocoding stats
|
|
curl http://localhost:4000/api/map/geocoding/stats \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Shows:
|
|
# {
|
|
# "totalAttempts": 1523,
|
|
# "successful": 1450,
|
|
# "failed": 73,
|
|
# "byProvider": {
|
|
# "google": { "attempts": 500, "successes": 480 },
|
|
# "nominatim": { "attempts": 600, "successes": 570 }
|
|
# }
|
|
# }
|
|
```
|
|
|
|
**Solution 5: Wait and retry**
|
|
|
|
Rate limits reset after time:
|
|
- **Nominatim**: 1 request/second (resets immediately)
|
|
- **Google**: 50 requests/second (resets after 1 second)
|
|
- **Mapbox**: 600 requests/minute (resets after 1 minute)
|
|
|
|
```bash
|
|
# Retry geocoding after wait
|
|
curl -X POST http://localhost:4000/api/map/locations/LOCATION_ID/geocode \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **API key monitoring** - Alert on API key errors
|
|
- **Rate limit tracking** - Monitor usage against limits
|
|
- **Provider rotation** - Distribute load across providers
|
|
- **Graceful degradation** - Continue with partial results
|
|
|
|
---
|
|
|
|
### Low Confidence Results
|
|
|
|
**Severity:** 🟢 Low
|
|
|
|
#### Symptoms
|
|
|
|
Geocoding succeeds but coordinates seem wrong or imprecise.
|
|
|
|
Example:
|
|
- Address: "123 Main Street, Toronto"
|
|
- Geocoded to: Center of Toronto (not specific address)
|
|
|
|
#### Common Causes
|
|
|
|
1. **Ambiguous address** - Multiple matches
|
|
2. **Incomplete address** - Missing street number
|
|
3. **Rural address** - Only city-level precision
|
|
4. **Provider limitation** - Provider doesn't have precise data
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Check geocoding confidence**
|
|
|
|
```bash
|
|
# View location details
|
|
curl http://localhost:4000/api/map/locations/LOCATION_ID \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Response includes:
|
|
# {
|
|
# "geocodingProvider": "nominatim",
|
|
# "geocodingConfidence": "low", # or "high", "medium"
|
|
# "latitude": 43.65,
|
|
# "longitude": -79.38
|
|
# }
|
|
```
|
|
|
|
**Solution 2: Add more detail to address**
|
|
|
|
```bash
|
|
# Low confidence:
|
|
"Main Street, Toronto"
|
|
|
|
# Higher confidence:
|
|
"123 Main Street, Toronto, ON M5H 2N2"
|
|
|
|
# Best confidence:
|
|
"123 Main Street, Toronto, Ontario M5H 2N2, Canada"
|
|
```
|
|
|
|
**Solution 3: Use postal code geocoding**
|
|
|
|
For Canadian addresses, postal code is often more accurate:
|
|
|
|
```bash
|
|
# Update location with postal code
|
|
UPDATE "Location"
|
|
SET "postalCode" = 'M5H 2N2'
|
|
WHERE id = 'LOCATION_ID';
|
|
|
|
# Re-geocode (will use postal code)
|
|
curl -X POST http://localhost:4000/api/map/locations/LOCATION_ID/geocode \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
```
|
|
|
|
**Solution 4: Manually verify on map**
|
|
|
|
In LocationsPage:
|
|
1. Click location row
|
|
2. View on map
|
|
3. If wrong, manually drag marker to correct location
|
|
4. Save
|
|
|
|
**Solution 5: Flag for review**
|
|
|
|
```bash
|
|
# Mark low-confidence results for manual review
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT id, address, \"geocodingConfidence\"
|
|
FROM \"Location\"
|
|
WHERE \"geocodingConfidence\" = 'low'
|
|
ORDER BY \"createdAt\" DESC
|
|
LIMIT 50;"
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Confidence tracking** - Store confidence score
|
|
- **Manual review queue** - Review low-confidence results
|
|
- **Address validation** - Validate format before geocoding
|
|
- **Postal code priority** - Use postal code when available
|
|
|
|
---
|
|
|
|
### Rate Limit Exceeded
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
```
|
|
ERROR Geocoding rate limit exceeded for provider: google
|
|
WARN Retrying with next provider: mapbox
|
|
```
|
|
|
|
Or:
|
|
|
|
```
|
|
ERROR 429 Too Many Requests from https://maps.googleapis.com/
|
|
```
|
|
|
|
#### Common Causes
|
|
|
|
1. **Bulk import** - Geocoding thousands of addresses at once
|
|
2. **No API key** - Free tier has lower limits
|
|
3. **Shared IP** - Multiple users on same IP
|
|
4. **Testing** - Repeated manual geocodes
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Check rate limits**
|
|
|
|
Per-provider limits:
|
|
|
|
| Provider | Free Tier | With API Key |
|
|
|----------|-----------|--------------|
|
|
| Nominatim | 1/sec | N/A |
|
|
| Google | N/A | 50/sec (or paid limit) |
|
|
| Mapbox | N/A | 600/min |
|
|
| ArcGIS | 1000/day | Varies |
|
|
| Photon | Unlimited | N/A |
|
|
| HERE | N/A | Varies by plan |
|
|
|
|
**Solution 2: Use geocoding queue**
|
|
|
|
For bulk operations:
|
|
|
|
```bash
|
|
# Queue all ungeocoded locations
|
|
curl -X POST http://localhost:4000/api/map/locations/queue-geocoding \
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"batchSize": 100}'
|
|
|
|
# Queue processes at rate-limit-safe speed
|
|
```
|
|
|
|
**Solution 3: Add API keys**
|
|
|
|
```bash
|
|
# In .env
|
|
GOOGLE_GEOCODING_API_KEY=your-key-here
|
|
MAPBOX_API_KEY=your-key-here
|
|
|
|
# Restart API
|
|
docker compose restart api
|
|
```
|
|
|
|
**Solution 4: Distribute across providers**
|
|
|
|
```bash
|
|
# Check provider usage
|
|
curl http://localhost:4000/api/map/geocoding/stats \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# If one provider is overused, system auto-rotates to others
|
|
```
|
|
|
|
**Solution 5: Wait and retry**
|
|
|
|
```bash
|
|
# Wait for rate limit window to reset
|
|
# Nominatim: 1 second
|
|
# Google: Check quota reset time
|
|
# Mapbox: 1 minute
|
|
|
|
# Retry failed geocodes
|
|
curl -X POST http://localhost:4000/api/map/locations/retry-failed \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **API keys** - Use paid tiers for higher limits
|
|
- **Queue system** - Respect rate limits automatically
|
|
- **Provider rotation** - Distribute load
|
|
- **Monitor usage** - Alert when approaching limits
|
|
|
|
---
|
|
|
|
## Map Display Issues
|
|
|
|
### Map Not Loading
|
|
|
|
**Severity:** 🟠 High
|
|
|
|
#### Symptoms
|
|
|
|
Map container shows blank white/gray box. No tiles loaded.
|
|
|
|
Browser console:
|
|
```
|
|
Error loading tile: https://tile.openstreetmap.org/...
|
|
Failed to load resource: net::ERR_BLOCKED_BY_CLIENT
|
|
```
|
|
|
|
#### Common Causes
|
|
|
|
1. **Ad blocker** - Blocking OSM tile requests
|
|
2. **Network issue** - Can't reach tile server
|
|
3. **CSP headers** - Content Security Policy blocking
|
|
4. **Leaflet CSS missing** - Styles not imported
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Disable ad blocker**
|
|
|
|
1. Disable ad blocker for your site
|
|
2. Or whitelist `*.openstreetmap.org`
|
|
3. Refresh page
|
|
|
|
**Solution 2: Check network**
|
|
|
|
```bash
|
|
# Test tile server
|
|
curl -I https://tile.openstreetmap.org/0/0/0.png
|
|
|
|
# Should return 200 OK
|
|
# If fails, network or DNS issue
|
|
```
|
|
|
|
**Solution 3: Verify Leaflet CSS**
|
|
|
|
In map component file:
|
|
|
|
```typescript
|
|
// Must import Leaflet CSS
|
|
import 'leaflet/dist/leaflet.css';
|
|
```
|
|
|
|
Check in browser DevTools:
|
|
- Elements tab → Check if `.leaflet-container` has styles
|
|
- Network tab → Check if `leaflet.css` loaded
|
|
|
|
**Solution 4: Check CSP headers**
|
|
|
|
In `nginx/conf.d/default.conf`:
|
|
|
|
```nginx
|
|
# Allow OSM tiles
|
|
add_header Content-Security-Policy "... img-src 'self' data: https://*.openstreetmap.org;";
|
|
```
|
|
|
|
**Solution 5: Try alternative tile provider**
|
|
|
|
```typescript
|
|
// In map component
|
|
<TileLayer
|
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
// Or try Carto:
|
|
// url="https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"
|
|
/>
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Ad blocker warning** - Detect and show warning
|
|
- **Fallback tiles** - Multiple tile providers
|
|
- **Error boundaries** - Catch map loading errors
|
|
- **Clear documentation** - Document ad blocker issue
|
|
|
|
---
|
|
|
|
### Markers Not Appearing
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Map loads but location markers don't appear.
|
|
|
|
#### Common Causes
|
|
|
|
1. **No data** - No locations fetched
|
|
2. **Null coordinates** - Locations not geocoded
|
|
3. **Out of bounds** - Markers outside map view
|
|
4. **Rendering error** - React component error
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Check data loaded**
|
|
|
|
```javascript
|
|
// In browser console
|
|
console.log('Locations:', locations);
|
|
|
|
// Should show array of locations with lat/lng
|
|
// If empty or undefined, data not loaded
|
|
```
|
|
|
|
**Solution 2: Verify coordinates**
|
|
|
|
```bash
|
|
# Check locations have coordinates
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT COUNT(*) FROM \"Location\" WHERE latitude IS NOT NULL AND longitude IS NOT NULL;"
|
|
|
|
# If 0, no locations geocoded
|
|
```
|
|
|
|
**Solution 3: Zoom to markers**
|
|
|
|
```typescript
|
|
// In map component, fit bounds to markers
|
|
useEffect(() => {
|
|
if (locations.length > 0 && mapRef.current) {
|
|
const bounds = locations
|
|
.filter(l => l.latitude && l.longitude)
|
|
.map(l => [l.latitude, l.longitude]);
|
|
|
|
if (bounds.length > 0) {
|
|
mapRef.current.fitBounds(bounds, { padding: [50, 50] });
|
|
}
|
|
}
|
|
}, [locations]);
|
|
```
|
|
|
|
**Solution 4: Check marker rendering**
|
|
|
|
```typescript
|
|
// Verify CircleMarker component
|
|
{locations.map((location) => {
|
|
if (!location.latitude || !location.longitude) return null;
|
|
|
|
return (
|
|
<CircleMarker
|
|
key={location.id}
|
|
center={[location.latitude, location.longitude]}
|
|
radius={8}
|
|
// ...
|
|
/>
|
|
);
|
|
})}
|
|
```
|
|
|
|
**Solution 5: Check browser console**
|
|
|
|
Look for React errors:
|
|
```
|
|
Warning: Each child in a list should have a unique "key" prop
|
|
Error: Invalid latitude/longitude
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Data validation** - Ensure data has coordinates
|
|
- **Error boundaries** - Catch rendering errors
|
|
- **Loading states** - Show loading while fetching
|
|
- **Empty states** - Show message if no data
|
|
|
|
---
|
|
|
|
### Cuts Not Rendering
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Cut polygons don't appear on map.
|
|
|
|
#### Common Causes
|
|
|
|
1. **Invalid GeoJSON** - Malformed polygon data
|
|
2. **Wrong coordinate order** - GeoJSON uses [lng, lat], Leaflet uses [lat, lng]
|
|
3. **Self-intersecting polygon** - Invalid polygon geometry
|
|
4. **Out of bounds** - Polygon outside map view
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Validate GeoJSON**
|
|
|
|
```bash
|
|
# Check cut geometry
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT id, name, ST_AsGeoJSON(geometry) FROM \"Cut\" WHERE id = 'CUT_ID';"
|
|
|
|
# Verify format:
|
|
# {
|
|
# "type": "Polygon",
|
|
# "coordinates": [[[lng1, lat1], [lng2, lat2], ...]]
|
|
# }
|
|
```
|
|
|
|
**Solution 2: Convert coordinates**
|
|
|
|
```typescript
|
|
// GeoJSON uses [lng, lat]
|
|
const geojson = {
|
|
type: 'Polygon',
|
|
coordinates: [[[-79.38, 43.65], [-79.37, 43.65], ...]]
|
|
};
|
|
|
|
// Convert to Leaflet [lat, lng]
|
|
const leafletCoords = geojson.coordinates[0].map(([lng, lat]) => [lat, lng]);
|
|
```
|
|
|
|
**Solution 3: Check for self-intersection**
|
|
|
|
```sql
|
|
-- Validate polygon geometry
|
|
SELECT id, name, ST_IsValid(geometry) as is_valid
|
|
FROM "Cut"
|
|
WHERE NOT ST_IsValid(geometry);
|
|
|
|
-- If invalid, show reason
|
|
SELECT id, name, ST_IsValidReason(geometry)
|
|
FROM "Cut"
|
|
WHERE NOT ST_IsValid(geometry);
|
|
|
|
-- Fix with buffer(0)
|
|
UPDATE "Cut"
|
|
SET geometry = ST_Buffer(geometry, 0)
|
|
WHERE NOT ST_IsValid(geometry);
|
|
```
|
|
|
|
**Solution 4: Zoom to cut**
|
|
|
|
```typescript
|
|
// Fit map to cut bounds
|
|
useEffect(() => {
|
|
if (cut?.geometry && mapRef.current) {
|
|
const coords = cut.geometry.coordinates[0].map(([lng, lat]) => [lat, lng]);
|
|
const bounds = L.latLngBounds(coords);
|
|
mapRef.current.fitBounds(bounds, { padding: [50, 50] });
|
|
}
|
|
}, [cut]);
|
|
```
|
|
|
|
**Solution 5: Check Polygon component**
|
|
|
|
```typescript
|
|
// Verify Polygon rendering
|
|
<Polygon
|
|
positions={coords} // Array of [lat, lng]
|
|
pathOptions={{
|
|
color: '#3498db',
|
|
fillColor: '#3498db',
|
|
fillOpacity: 0.2
|
|
}}
|
|
/>
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Geometry validation** - Validate on save
|
|
- **Drawing tools** - Use validated drawing library
|
|
- **Import validation** - Check imported geometries
|
|
- **Error handling** - Gracefully handle invalid geometries
|
|
|
|
---
|
|
|
|
### GPS Not Working
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Geolocate button doesn't work or shows error.
|
|
|
|
Browser shows permission prompt but location never loads.
|
|
|
|
#### Common Causes
|
|
|
|
1. **HTTPS required** - Geolocation API requires HTTPS (or localhost)
|
|
2. **Permission denied** - User denied location permission
|
|
3. **GPS unavailable** - Device has no GPS
|
|
4. **Browser doesn't support** - Old browser
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Check HTTPS**
|
|
|
|
Geolocation API requires:
|
|
- **HTTPS** (https://)
|
|
- OR **localhost** (http://localhost)
|
|
- OR **127.0.0.1** (http://127.0.0.1)
|
|
|
|
```bash
|
|
# In production, ensure HTTPS
|
|
# Via Pangolin tunnel or Cloudflare
|
|
```
|
|
|
|
**Solution 2: Grant permission**
|
|
|
|
1. Click lock icon in address bar
|
|
2. Location → Allow
|
|
3. Refresh page
|
|
4. Try geolocate again
|
|
|
|
**Solution 3: Test geolocation API**
|
|
|
|
```javascript
|
|
// In browser console
|
|
navigator.geolocation.getCurrentPosition(
|
|
(pos) => console.log('Location:', pos.coords),
|
|
(err) => console.error('Error:', err)
|
|
);
|
|
|
|
// Errors:
|
|
// PERMISSION_DENIED - User denied
|
|
// POSITION_UNAVAILABLE - GPS unavailable
|
|
// TIMEOUT - Taking too long
|
|
```
|
|
|
|
**Solution 4: Increase timeout**
|
|
|
|
```typescript
|
|
// In geolocate code
|
|
navigator.geolocation.getCurrentPosition(
|
|
successCallback,
|
|
errorCallback,
|
|
{
|
|
timeout: 10000, // 10 seconds (default: 5000)
|
|
enableHighAccuracy: true,
|
|
maximumAge: 0
|
|
}
|
|
);
|
|
```
|
|
|
|
**Solution 5: Fallback to IP geolocation**
|
|
|
|
```typescript
|
|
// If GPS fails, use IP-based location
|
|
const fallbackLocation = async () => {
|
|
const response = await fetch('https://ipapi.co/json/');
|
|
const data = await response.json();
|
|
return {
|
|
latitude: data.latitude,
|
|
longitude: data.longitude
|
|
};
|
|
};
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **HTTPS in production** - Use secure connection
|
|
- **Permission prompts** - Clear instructions
|
|
- **Fallback options** - IP geolocation as backup
|
|
- **Error handling** - User-friendly error messages
|
|
|
|
---
|
|
|
|
## Coordinate Issues
|
|
|
|
### Invalid Lat/Lng
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
```
|
|
Error: Invalid latitude/longitude values
|
|
```
|
|
|
|
Or markers appear in wrong location (ocean, wrong country).
|
|
|
|
#### Common Causes
|
|
|
|
1. **Swapped coordinates** - Latitude and longitude reversed
|
|
2. **Out of range** - Latitude > 90 or Longitude > 180
|
|
3. **Wrong sign** - Positive instead of negative (or vice versa)
|
|
4. **Decimal precision** - Too many/few decimal places
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Validate ranges**
|
|
|
|
Valid ranges:
|
|
- **Latitude**: -90 to 90
|
|
- **Longitude**: -180 to 180
|
|
|
|
```bash
|
|
# Find invalid coordinates
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT id, address, latitude, longitude
|
|
FROM \"Location\"
|
|
WHERE latitude < -90 OR latitude > 90
|
|
OR longitude < -180 OR longitude > 180;"
|
|
```
|
|
|
|
**Solution 2: Check coordinate order**
|
|
|
|
```bash
|
|
# Common mistake: swapped lat/lng
|
|
# Toronto should be:
|
|
# Latitude: 43.65 (positive, North)
|
|
# Longitude: -79.38 (negative, West)
|
|
|
|
# If showing as 79.38, -43.65, they're swapped
|
|
|
|
# Fix:
|
|
UPDATE "Location"
|
|
SET latitude = longitude, longitude = latitude
|
|
WHERE id = 'LOCATION_ID';
|
|
```
|
|
|
|
**Solution 3: Verify hemisphere**
|
|
|
|
For North American locations:
|
|
- **Latitude**: Positive (North)
|
|
- **Longitude**: Negative (West)
|
|
|
|
```bash
|
|
# If US/Canada location has positive longitude, wrong sign
|
|
UPDATE "Location"
|
|
SET longitude = longitude * -1
|
|
WHERE country = 'Canada' AND longitude > 0;
|
|
```
|
|
|
|
**Solution 4: Check decimal precision**
|
|
|
|
```bash
|
|
# Good precision (6 decimals ≈ 0.1m accuracy):
|
|
Latitude: 43.651234
|
|
Longitude: -79.381234
|
|
|
|
# Too few decimals (imprecise):
|
|
Latitude: 43.65
|
|
Longitude: -79.38
|
|
|
|
# Too many decimals (unnecessary):
|
|
Latitude: 43.651234567890
|
|
Longitude: -79.381234567890
|
|
```
|
|
|
|
**Solution 5: Visual verification**
|
|
|
|
1. Open Google Maps
|
|
2. Enter coordinates: `43.651234, -79.381234`
|
|
3. Verify location matches address
|
|
4. If wrong, get correct coordinates from Google Maps
|
|
|
|
#### Prevention
|
|
|
|
- **Coordinate validation** - Check ranges before save
|
|
- **Visual preview** - Show on map before save
|
|
- **Import validation** - Validate imported coordinates
|
|
- **Decimal precision** - Round to 6 decimals
|
|
|
|
---
|
|
|
|
### Out of Bounds Coordinates
|
|
|
|
**Severity:** 🟢 Low
|
|
|
|
#### Symptoms
|
|
|
|
Markers appear outside expected area (different country/continent).
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Set map bounds**
|
|
|
|
```typescript
|
|
// Limit map to expected region
|
|
const bounds = L.latLngBounds(
|
|
[41.0, -95.0], // Southwest corner
|
|
[50.0, -74.0] // Northeast corner (covers eastern Canada/US)
|
|
);
|
|
|
|
<MapContainer
|
|
maxBounds={bounds}
|
|
maxBoundsViscosity={1.0}
|
|
// ...
|
|
/>
|
|
```
|
|
|
|
**Solution 2: Filter locations by bounds**
|
|
|
|
```typescript
|
|
// Only show locations in expected region
|
|
const filteredLocations = locations.filter(location => {
|
|
return location.latitude >= 41 && location.latitude <= 50 &&
|
|
location.longitude >= -95 && location.longitude <= -74;
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
### Projection Errors (NAR Data)
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Locations imported from NAR data appear in wrong place.
|
|
|
|
#### Common Causes
|
|
|
|
1. **Wrong projection** - NAR uses EPSG:3347 (Lambert), not WGS84
|
|
2. **Missing conversion** - Coordinates not converted to lat/lng
|
|
3. **Coordinate swap** - BG_X and BG_Y reversed
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Verify NAR import uses proj4**
|
|
|
|
In `api/src/modules/map/locations/nar-import.service.ts`:
|
|
|
|
```typescript
|
|
import proj4 from 'proj4';
|
|
|
|
// Define EPSG:3347 (NAR projection)
|
|
proj4.defs('EPSG:3347',
|
|
'+proj=lcc +lat_0=63.390675 +lon_0=-91.86666666666666 ' +
|
|
'+lat_1=49 +lat_2=77 +x_0=6200000 +y_0=3000000 ' +
|
|
'+ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs'
|
|
);
|
|
|
|
// Convert
|
|
const [longitude, latitude] = proj4('EPSG:3347', 'WGS84', [bgX, bgY]);
|
|
```
|
|
|
|
**Solution 2: Check coordinate order**
|
|
|
|
NAR Address files:
|
|
- **BG_X**: Easting (X coordinate in meters)
|
|
- **BG_Y**: Northing (Y coordinate in meters)
|
|
|
|
Conversion order: `[BG_X, BG_Y]` → `[longitude, latitude]`
|
|
|
|
**Solution 3: Verify conversion**
|
|
|
|
```bash
|
|
# Test conversion manually
|
|
docker compose exec api node -e "
|
|
const proj4 = require('proj4');
|
|
proj4.defs('EPSG:3347', '+proj=lcc +lat_0=63.390675 +lon_0=-91.86666666666666 +lat_1=49 +lat_2=77 +x_0=6200000 +y_0=3000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
|
|
|
|
// Example Toronto coordinates in EPSG:3347:
|
|
const [lng, lat] = proj4('EPSG:3347', 'WGS84', [6458123, 3534567]);
|
|
console.log('Lat:', lat, 'Lng:', lng);
|
|
// Should be approximately: Lat: 43.65 Lng: -79.38
|
|
"
|
|
```
|
|
|
|
**Solution 4: Re-import NAR data**
|
|
|
|
If imported incorrectly:
|
|
|
|
```bash
|
|
# Delete bad data
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "DELETE FROM \"Location\" WHERE \"importSource\" = 'NAR';"
|
|
|
|
# Re-import with correct projection
|
|
# Via admin UI: /app/map/locations → NAR Import tab
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Projection validation** - Test conversion on sample data
|
|
- **Visual verification** - Show import preview on map
|
|
- **Documentation** - Document NAR projection requirements
|
|
- **Import validation** - Check coordinates are in expected range
|
|
|
|
---
|
|
|
|
## Queue Issues
|
|
|
|
### Geocoding Queue Stuck
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Locations remain ungeocoded even though queue is running.
|
|
|
|
Queue shows jobs but they never process.
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Check queue status**
|
|
|
|
```bash
|
|
# View queue stats
|
|
curl http://localhost:4000/api/map/geocoding/queue/stats \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Shows:
|
|
# {
|
|
# "waiting": 150,
|
|
# "active": 0, # Should be > 0 if processing
|
|
# "completed": 2500,
|
|
# "failed": 25
|
|
# }
|
|
```
|
|
|
|
**Solution 2: Check worker is running**
|
|
|
|
```bash
|
|
# Worker should log processing
|
|
docker compose logs api | grep -i "geocoding worker\|processing geocode"
|
|
|
|
# Should show:
|
|
# Geocoding worker started
|
|
# Processing geocode job for location: abc-123
|
|
```
|
|
|
|
**Solution 3: Restart queue worker**
|
|
|
|
```bash
|
|
# Restart API (restarts worker)
|
|
docker compose restart api
|
|
|
|
# Check worker started
|
|
docker compose logs api | grep "Geocoding worker started"
|
|
```
|
|
|
|
**Solution 4: Check Redis connection**
|
|
|
|
```bash
|
|
# Test Redis
|
|
docker compose exec redis redis-cli -a YOUR_REDIS_PASSWORD ping
|
|
# Should return: PONG
|
|
|
|
# Check queue keys
|
|
docker compose exec redis redis-cli -a YOUR_REDIS_PASSWORD keys "bull:geocoding:*"
|
|
```
|
|
|
|
**Solution 5: Manually process stuck jobs**
|
|
|
|
```bash
|
|
# Retry failed jobs
|
|
curl -X POST http://localhost:4000/api/map/geocoding/queue/retry-failed \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Clean stuck jobs
|
|
curl -X POST http://localhost:4000/api/map/geocoding/queue/clean \
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
-d '{"status": "failed", "grace": 86400000}' # Clean failed jobs older than 1 day
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Health checks** - Monitor worker health
|
|
- **Dead letter queue** - Move repeatedly failed jobs
|
|
- **Alerting** - Alert if queue backed up
|
|
- **Auto-restart** - Restart worker if stuck
|
|
|
|
---
|
|
|
|
### Jobs Failing
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Queue shows high failed job count.
|
|
|
|
Locations remain ungeocoded with error status.
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: View failed jobs**
|
|
|
|
```bash
|
|
# Get failed job details
|
|
curl http://localhost:4000/api/map/geocoding/queue/failed \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Shows:
|
|
# [
|
|
# {
|
|
# "id": "123",
|
|
# "data": { "locationId": "abc", "address": "..." },
|
|
# "failedReason": "All providers failed",
|
|
# "attemptsMade": 3
|
|
# }
|
|
# ]
|
|
```
|
|
|
|
**Solution 2: Check error patterns**
|
|
|
|
```bash
|
|
# Common failure reasons
|
|
docker compose logs api | grep "Geocoding failed" | sort | uniq -c
|
|
|
|
# Example output:
|
|
# 45 Geocoding failed: Rate limit exceeded
|
|
# 12 Geocoding failed: No results found
|
|
# 3 Geocoding failed: Network error
|
|
```
|
|
|
|
**Solution 3: Retry with different settings**
|
|
|
|
```bash
|
|
# Retry with longer timeout
|
|
curl -X POST http://localhost:4000/api/map/locations/LOCATION_ID/geocode \
|
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"timeout": 30000}' # 30 seconds
|
|
```
|
|
|
|
**Solution 4: Manual intervention**
|
|
|
|
For repeatedly failing addresses:
|
|
|
|
1. Open LocationsPage
|
|
2. Find failed locations
|
|
3. Review address (fix typos)
|
|
4. Manually set coordinates if needed
|
|
5. Or delete if invalid
|
|
|
|
#### Prevention
|
|
|
|
- **Retry logic** - Auto-retry with exponential backoff
|
|
- **Error categorization** - Permanent vs transient failures
|
|
- **Manual review queue** - Flag for manual review after N attempts
|
|
- **Address validation** - Validate before geocoding
|
|
|
|
---
|
|
|
|
## Performance Issues
|
|
|
|
### Slow Geocoding
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Geocoding takes 5-10+ seconds per address.
|
|
|
|
Bulk imports very slow.
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Use faster providers first**
|
|
|
|
Provider speed (fastest to slowest):
|
|
1. Google (with API key) - ~200ms
|
|
2. Mapbox (with API key) - ~300ms
|
|
3. Nominatim - ~500ms
|
|
4. ArcGIS - ~800ms
|
|
5. Photon - ~1000ms
|
|
6. HERE - ~400ms
|
|
|
|
Configure in `api/src/modules/map/geocoding/geocoding.service.ts`.
|
|
|
|
**Solution 2: Increase concurrency**
|
|
|
|
In geocoding queue worker:
|
|
|
|
```typescript
|
|
// Increase concurrent geocoding
|
|
const worker = new Worker('geocoding', processor, {
|
|
concurrency: 5, // Process 5 at a time (default: 1)
|
|
limiter: {
|
|
max: 50, // Max 50 jobs per second
|
|
duration: 1000
|
|
}
|
|
});
|
|
```
|
|
|
|
**Solution 3: Use bulk geocoding APIs**
|
|
|
|
Some providers offer batch geocoding:
|
|
|
|
```bash
|
|
# Google Batch Geocoding (requires Business plan)
|
|
# Can geocode up to 100 addresses in one request
|
|
```
|
|
|
|
**Solution 4: Cache results**
|
|
|
|
```typescript
|
|
// Cache geocoding results in Redis
|
|
const cacheKey = `geocode:${address}`;
|
|
const cached = await redis.get(cacheKey);
|
|
|
|
if (cached) {
|
|
return JSON.parse(cached);
|
|
}
|
|
|
|
const result = await geocode(address);
|
|
await redis.setex(cacheKey, 86400, JSON.stringify(result)); // Cache 24h
|
|
return result;
|
|
```
|
|
|
|
**Solution 5: Parallel processing**
|
|
|
|
```typescript
|
|
// Geocode multiple addresses in parallel
|
|
const addresses = ['123 Main St', '456 Oak Ave', ...];
|
|
|
|
const results = await Promise.all(
|
|
addresses.map(address => geocode(address))
|
|
);
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Queue system** - Don't block UI on geocoding
|
|
- **Paid tiers** - Faster with API keys
|
|
- **Caching** - Cache frequent addresses
|
|
- **Parallel processing** - Process multiple at once
|
|
|
|
---
|
|
|
|
### Too Many API Calls
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
High API usage on Google/Mapbox.
|
|
|
|
Approaching or exceeding quota.
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Monitor usage**
|
|
|
|
```bash
|
|
# Check geocoding stats
|
|
curl http://localhost:4000/api/map/geocoding/stats \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Track API costs:
|
|
# Google: $5 per 1000 requests (after 40k free/month)
|
|
# Mapbox: $0.50 per 1000 requests (after 100k free/month)
|
|
```
|
|
|
|
**Solution 2: Use free providers first**
|
|
|
|
Reorder provider priority:
|
|
|
|
```typescript
|
|
// In geocodingService.ts
|
|
const providers = [
|
|
'nominatim', // Free (try first)
|
|
'photon', // Free
|
|
'arcgis', // Free (1000/day)
|
|
'google', // Paid (use only if others fail)
|
|
'mapbox', // Paid
|
|
'here' // Paid
|
|
];
|
|
```
|
|
|
|
**Solution 3: Cache aggressively**
|
|
|
|
```typescript
|
|
// Cache geocoding results permanently
|
|
const cacheKey = `geocode:${normalizeAddress(address)}`;
|
|
const cached = await redis.get(cacheKey);
|
|
|
|
if (cached) {
|
|
return JSON.parse(cached);
|
|
}
|
|
|
|
const result = await geocode(address);
|
|
await redis.set(cacheKey, JSON.stringify(result)); // No expiration
|
|
return result;
|
|
```
|
|
|
|
**Solution 4: Deduplicate requests**
|
|
|
|
```bash
|
|
# Before geocoding, check if address already geocoded
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT latitude, longitude FROM \"Location\"
|
|
WHERE LOWER(address) = LOWER('123 Main Street, Toronto')
|
|
AND latitude IS NOT NULL
|
|
LIMIT 1;"
|
|
|
|
# If exists, copy coordinates instead of geocoding again
|
|
```
|
|
|
|
**Solution 5: Set quota alerts**
|
|
|
|
In Google Cloud Console:
|
|
1. Navigate to Geocoding API
|
|
2. Set quota alerts (e.g., 80% of limit)
|
|
3. Receive email before exceeding quota
|
|
|
|
#### Prevention
|
|
|
|
- **Cache everything** - Never geocode same address twice
|
|
- **Free providers first** - Use paid only as fallback
|
|
- **Quota monitoring** - Alert before exceeding
|
|
- **Cost tracking** - Monitor API costs monthly
|
|
|
|
---
|
|
|
|
## Data Quality
|
|
|
|
### Duplicate Locations
|
|
|
|
**Severity:** 🟢 Low
|
|
|
|
#### Symptoms
|
|
|
|
Same address appears multiple times in locations table.
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Find duplicates**
|
|
|
|
```bash
|
|
# Find duplicate addresses
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT address, COUNT(*), array_agg(id)
|
|
FROM \"Location\"
|
|
GROUP BY LOWER(address)
|
|
HAVING COUNT(*) > 1;"
|
|
```
|
|
|
|
**Solution 2: Merge duplicates**
|
|
|
|
```bash
|
|
# Keep oldest, delete newer
|
|
# (After reassigning foreign keys to kept record)
|
|
DELETE FROM "Location" AS l1
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM "Location" AS l2
|
|
WHERE LOWER(l2.address) = LOWER(l1.address)
|
|
AND l2."createdAt" < l1."createdAt"
|
|
);
|
|
```
|
|
|
|
**Solution 3: Add unique constraint**
|
|
|
|
```prisma
|
|
model Location {
|
|
id String @id @default(uuid())
|
|
address String
|
|
|
|
@@unique([address]) // Prevent duplicates
|
|
}
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Unique constraints** - Database prevents duplicates
|
|
- **Upsert logic** - Update if exists, create if not
|
|
- **Import validation** - Check for duplicates before import
|
|
- **Case-insensitive comparison** - Normalize before checking
|
|
|
|
---
|
|
|
|
### Ungeocoded Locations
|
|
|
|
**Severity:** 🟡 Medium
|
|
|
|
#### Symptoms
|
|
|
|
Many locations with `null` latitude/longitude.
|
|
|
|
#### Solutions
|
|
|
|
**Solution 1: Count ungeocoded**
|
|
|
|
```bash
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT COUNT(*) FROM \"Location\" WHERE latitude IS NULL;"
|
|
```
|
|
|
|
**Solution 2: Queue all ungeocoded**
|
|
|
|
```bash
|
|
# Via API
|
|
curl -X POST http://localhost:4000/api/map/locations/queue-geocoding \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Queues all locations with null coordinates
|
|
```
|
|
|
|
**Solution 3: View on Data Quality Dashboard**
|
|
|
|
Navigate to `/app/map/data-quality`:
|
|
|
|
- Shows geocoding rate
|
|
- Lists ungeocoded locations
|
|
- Allows bulk geocoding
|
|
|
|
**Solution 4: Export ungeocoded for manual review**
|
|
|
|
```bash
|
|
# Export to CSV
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "COPY (SELECT id, address, city, \"postalCode\" FROM \"Location\"
|
|
WHERE latitude IS NULL) TO STDOUT WITH CSV HEADER" > ungeocoded.csv
|
|
```
|
|
|
|
#### Prevention
|
|
|
|
- **Geocode on create** - Auto-geocode new locations
|
|
- **Required coordinates** - Don't allow creating without geocoding
|
|
- **Dashboard monitoring** - Track geocoding rate
|
|
- **Regular cleanup** - Periodic geocoding of ungeocoded
|
|
|
|
---
|
|
|
|
## Useful Commands
|
|
|
|
### Geocoding Operations
|
|
|
|
```bash
|
|
# Geocode single location
|
|
curl -X POST http://localhost:4000/api/map/locations/LOCATION_ID/geocode \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Bulk geocode via queue
|
|
curl -X POST http://localhost:4000/api/map/locations/queue-geocoding \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Check geocoding stats
|
|
curl http://localhost:4000/api/map/geocoding/stats \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
|
|
# Retry failed geocodes
|
|
curl -X POST http://localhost:4000/api/map/geocoding/queue/retry-failed \
|
|
-H "Authorization: Bearer YOUR_TOKEN"
|
|
```
|
|
|
|
### Database Queries
|
|
|
|
```bash
|
|
# Count by geocoding status
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT
|
|
COUNT(*) FILTER (WHERE latitude IS NOT NULL) as geocoded,
|
|
COUNT(*) FILTER (WHERE latitude IS NULL) as ungeocoded
|
|
FROM \"Location\";"
|
|
|
|
# List ungeocoded
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT id, address FROM \"Location\"
|
|
WHERE latitude IS NULL
|
|
LIMIT 50;"
|
|
|
|
# Geocoding provider stats
|
|
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
|
-c "SELECT \"geocodingProvider\", COUNT(*)
|
|
FROM \"Location\"
|
|
WHERE \"geocodingProvider\" IS NOT NULL
|
|
GROUP BY \"geocodingProvider\";"
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
### Geocoding Documentation
|
|
- [Geocoding Issues](geocoding-issues.md) - This guide
|
|
- [Locations Feature](../features/map/locations.md) - Location management
|
|
- [Data Quality Dashboard](../user-guides/data-quality-dashboard.md) - Monitoring geocoding
|
|
|
|
### Other Troubleshooting
|
|
- [Common Errors](common-errors.md) - General errors
|
|
- [Performance Optimization](performance-optimization.md) - Speed improvements
|
|
|
|
### External Resources
|
|
- [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/)
|
|
- [Google Geocoding API](https://developers.google.com/maps/documentation/geocoding)
|
|
- [Mapbox Geocoding API](https://docs.mapbox.com/api/search/geocoding/)
|
|
- [Leaflet Documentation](https://leafletjs.com/)
|
|
|
|
---
|
|
|
|
**Last Updated:** February 2026
|
|
**Version:** V2.0
|
|
**Status:** Complete
|