Initial commit: Northern Thailand Ping River Monitor v3.1.0
Some checks failed
Security & Dependency Updates / Dependency Security Scan (push) Successful in 29s
Security & Dependency Updates / Docker Security Scan (push) Failing after 53s
Security & Dependency Updates / License Compliance (push) Successful in 13s
Security & Dependency Updates / Check for Dependency Updates (push) Successful in 19s
Security & Dependency Updates / Code Quality Metrics (push) Successful in 11s
Security & Dependency Updates / Security Summary (push) Successful in 7s
Some checks failed
Security & Dependency Updates / Dependency Security Scan (push) Successful in 29s
Security & Dependency Updates / Docker Security Scan (push) Failing after 53s
Security & Dependency Updates / License Compliance (push) Successful in 13s
Security & Dependency Updates / Check for Dependency Updates (push) Successful in 19s
Security & Dependency Updates / Code Quality Metrics (push) Successful in 11s
Security & Dependency Updates / Security Summary (push) Successful in 7s
Features: - Real-time water level monitoring for Ping River Basin (16 stations) - Coverage from Chiang Dao to Nakhon Sawan in Northern Thailand - FastAPI web interface with interactive dashboard and station management - Multi-database support (SQLite, MySQL, PostgreSQL, InfluxDB, VictoriaMetrics) - Comprehensive monitoring with health checks and metrics collection - Docker deployment with Grafana integration - Production-ready architecture with enterprise-grade observability CI/CD & Automation: - Complete Gitea Actions workflows for CI/CD, security, and releases - Multi-Python version testing (3.9-3.12) - Multi-architecture Docker builds (amd64, arm64) - Daily security scanning and dependency monitoring - Automated documentation generation - Performance testing and validation Production Ready: - Type safety with Pydantic models and comprehensive type hints - Data validation layer with range checking and error handling - Rate limiting and request tracking for API protection - Enhanced logging with rotation, colors, and performance metrics - Station management API for dynamic CRUD operations - Comprehensive documentation and deployment guides Technical Stack: - Python 3.9+ with FastAPI and Pydantic - Multi-database architecture with adapter pattern - Docker containerization with multi-stage builds - Grafana dashboards for visualization - Gitea Actions for CI/CD automation - Enterprise monitoring and alerting Ready for deployment to B4L infrastructure!
This commit is contained in:
475
docs/GEOLOCATION_GUIDE.md
Normal file
475
docs/GEOLOCATION_GUIDE.md
Normal file
@@ -0,0 +1,475 @@
|
||||
# Geolocation Support for Grafana Geomap
|
||||
|
||||
This guide explains the geolocation functionality added to the Thailand Water Monitor for use with Grafana's geomap visualization.
|
||||
|
||||
## ✅ **Implemented Features**
|
||||
|
||||
### **Database Schema Updates**
|
||||
All database adapters now support geolocation fields:
|
||||
- **latitude**: Decimal latitude coordinates (DECIMAL(10,8) for SQL, REAL for SQLite)
|
||||
- **longitude**: Decimal longitude coordinates (DECIMAL(11,8) for SQL, REAL for SQLite)
|
||||
- **geohash**: Geohash string for efficient spatial indexing (VARCHAR(20)/TEXT)
|
||||
|
||||
### **Station Data Enhancement**
|
||||
Station mapping now includes geolocation fields:
|
||||
```python
|
||||
'8': {
|
||||
'code': 'P.1',
|
||||
'thai_name': 'สะพานนวรัฐ',
|
||||
'english_name': 'Nawarat Bridge',
|
||||
'latitude': 15.6944, # Decimal degrees
|
||||
'longitude': 100.2028, # Decimal degrees
|
||||
'geohash': 'w5q6uuhvfcfp25' # Geohash for P.1
|
||||
}
|
||||
```
|
||||
|
||||
## 🗄️ **Database Schema**
|
||||
|
||||
### **Updated Stations Table**
|
||||
```sql
|
||||
CREATE TABLE stations (
|
||||
id INTEGER PRIMARY KEY,
|
||||
station_code TEXT UNIQUE NOT NULL,
|
||||
thai_name TEXT NOT NULL,
|
||||
english_name TEXT NOT NULL,
|
||||
latitude REAL, -- NEW: Latitude coordinate
|
||||
longitude REAL, -- NEW: Longitude coordinate
|
||||
geohash TEXT, -- NEW: Geohash for spatial indexing
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### **Database Support**
|
||||
- ✅ **SQLite**: REAL columns for coordinates, TEXT for geohash
|
||||
- ✅ **PostgreSQL**: DECIMAL(10,8) and DECIMAL(11,8) for coordinates, VARCHAR(20) for geohash
|
||||
- ✅ **MySQL**: DECIMAL(10,8) and DECIMAL(11,8) for coordinates, VARCHAR(20) for geohash
|
||||
- ✅ **VictoriaMetrics**: Geolocation data included in metric labels
|
||||
|
||||
## 📊 **Current Station Data**
|
||||
|
||||
### **P.1 - Nawarat Bridge (Sample)**
|
||||
- **Station Code**: P.1
|
||||
- **Thai Name**: สะพานนวรัฐ
|
||||
- **English Name**: Nawarat Bridge
|
||||
- **Latitude**: 15.6944
|
||||
- **Longitude**: 100.2028
|
||||
- **Geohash**: w5q6uuhvfcfp25
|
||||
|
||||
### **Remaining Stations**
|
||||
The following stations are ready for geolocation data when coordinates become available:
|
||||
- P.20 - บ้านเชียงดาว (Ban Chiang Dao)
|
||||
- P.75 - บ้านช่อแล (Ban Chai Lat)
|
||||
- P.92 - บ้านเมืองกึ๊ด (Ban Muang Aut)
|
||||
- P.4A - บ้านแม่แตง (Ban Mae Taeng)
|
||||
- P.67 - บ้านแม่แต (Ban Tae)
|
||||
- P.21 - บ้านริมใต้ (Ban Rim Tai)
|
||||
- P.103 - สะพานวงแหวนรอบ 3 (Ring Bridge 3)
|
||||
- P.82 - บ้านสบวิน (Ban Sob win)
|
||||
- P.84 - บ้านพันตน (Ban Panton)
|
||||
- P.81 - บ้านโป่ง (Ban Pong)
|
||||
- P.5 - สะพานท่านาง (Tha Nang Bridge)
|
||||
- P.77 - บ้านสบแม่สะป๊วด (Baan Sop Mae Sapuord)
|
||||
- P.87 - บ้านป่าซาง (Ban Pa Sang)
|
||||
- P.76 - บ้านแม่อีไฮ (Banb Mae I Hai)
|
||||
- P.85 - บ้านหล่ายแก้ว (Baan Lai Kaew)
|
||||
|
||||
## 🗺️ **Grafana Geomap Integration**
|
||||
|
||||
### **Data Source Configuration**
|
||||
The geolocation data is automatically included in all database queries and can be used directly in Grafana:
|
||||
|
||||
#### **SQLite/PostgreSQL/MySQL Query Example**
|
||||
```sql
|
||||
SELECT
|
||||
m.timestamp,
|
||||
s.station_code,
|
||||
s.english_name,
|
||||
s.thai_name,
|
||||
s.latitude,
|
||||
s.longitude,
|
||||
s.geohash,
|
||||
m.water_level,
|
||||
m.discharge,
|
||||
m.discharge_percent
|
||||
FROM water_measurements m
|
||||
JOIN stations s ON m.station_id = s.id
|
||||
WHERE s.latitude IS NOT NULL
|
||||
AND s.longitude IS NOT NULL
|
||||
ORDER BY m.timestamp DESC
|
||||
```
|
||||
|
||||
#### **VictoriaMetrics Query Example**
|
||||
```promql
|
||||
water_level{latitude!="",longitude!=""}
|
||||
```
|
||||
|
||||
### **Geomap Panel Configuration**
|
||||
|
||||
#### **1. Create Geomap Panel**
|
||||
1. Add new panel in Grafana
|
||||
2. Select "Geomap" visualization
|
||||
3. Configure data source (SQLite/PostgreSQL/MySQL/VictoriaMetrics)
|
||||
|
||||
#### **2. Configure Location Fields**
|
||||
- **Latitude Field**: `latitude`
|
||||
- **Longitude Field**: `longitude`
|
||||
- **Alternative**: Use `geohash` field for geohash-based positioning
|
||||
|
||||
#### **3. Configure Display Options**
|
||||
- **Station Labels**: Use `station_code` or `english_name`
|
||||
- **Tooltip Information**: Include `thai_name`, `water_level`, `discharge`
|
||||
- **Color Mapping**: Map to `water_level` or `discharge_percent`
|
||||
|
||||
#### **4. Sample Geomap Configuration**
|
||||
```json
|
||||
{
|
||||
"type": "geomap",
|
||||
"title": "Thailand Water Stations",
|
||||
"targets": [
|
||||
{
|
||||
"rawSql": "SELECT latitude, longitude, station_code, english_name, water_level, discharge_percent FROM stations s JOIN water_measurements m ON s.id = m.station_id WHERE s.latitude IS NOT NULL AND m.timestamp = (SELECT MAX(timestamp) FROM water_measurements WHERE station_id = s.id)",
|
||||
"format": "table"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd",
|
||||
"field": "water_level"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 15.6944,
|
||||
"lon": 100.2028,
|
||||
"zoom": 8
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showZoom": true,
|
||||
"showAttribution": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"type": "markers",
|
||||
"config": {
|
||||
"size": {
|
||||
"field": "discharge_percent",
|
||||
"min": 5,
|
||||
"max": 20
|
||||
},
|
||||
"color": {
|
||||
"field": "water_level"
|
||||
},
|
||||
"showLegend": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 **Adding New Station Coordinates**
|
||||
|
||||
### **Method 1: Update Station Mapping**
|
||||
Edit `water_scraper_v3.py` and add coordinates to the station mapping:
|
||||
```python
|
||||
'1': {
|
||||
'code': 'P.20',
|
||||
'thai_name': 'บ้านเชียงดาว',
|
||||
'english_name': 'Ban Chiang Dao',
|
||||
'latitude': 19.3056, # Add actual coordinates
|
||||
'longitude': 98.9264, # Add actual coordinates
|
||||
'geohash': 'w4r6...' # Add actual geohash
|
||||
}
|
||||
```
|
||||
|
||||
### **Method 2: Direct Database Update**
|
||||
```sql
|
||||
UPDATE stations
|
||||
SET latitude = 19.3056, longitude = 98.9264, geohash = 'w4r6uuhvfcfp25'
|
||||
WHERE station_code = 'P.20';
|
||||
```
|
||||
|
||||
### **Method 3: Bulk Update Script**
|
||||
```python
|
||||
import sqlite3
|
||||
|
||||
coordinates = {
|
||||
'P.20': {'lat': 19.3056, 'lon': 98.9264, 'geohash': 'w4r6uuhvfcfp25'},
|
||||
'P.75': {'lat': 18.7756, 'lon': 99.1234, 'geohash': 'w4r5uuhvfcfp25'},
|
||||
# Add more stations...
|
||||
}
|
||||
|
||||
conn = sqlite3.connect('water_monitoring.db')
|
||||
cursor = conn.cursor()
|
||||
|
||||
for station_code, coords in coordinates.items():
|
||||
cursor.execute("""
|
||||
UPDATE stations
|
||||
SET latitude = ?, longitude = ?, geohash = ?
|
||||
WHERE station_code = ?
|
||||
""", (coords['lat'], coords['lon'], coords['geohash'], station_code))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
```
|
||||
|
||||
## 🌐 **Geohash Information**
|
||||
|
||||
### **What is Geohash?**
|
||||
Geohash is a geocoding system that represents geographic coordinates as a short alphanumeric string. It provides:
|
||||
- **Spatial Indexing**: Efficient spatial queries
|
||||
- **Proximity**: Similar geohashes indicate nearby locations
|
||||
- **Hierarchical**: Longer geohashes provide more precision
|
||||
|
||||
### **Geohash Precision Levels**
|
||||
- **5 characters**: ~2.4km precision
|
||||
- **6 characters**: ~610m precision
|
||||
- **7 characters**: ~76m precision
|
||||
- **8 characters**: ~19m precision
|
||||
- **9+ characters**: <5m precision
|
||||
|
||||
### **Example: P.1 Geohash**
|
||||
- **Geohash**: `w5q6uuhvfcfp25`
|
||||
- **Length**: 14 characters
|
||||
- **Precision**: Sub-meter accuracy
|
||||
- **Location**: Nawarat Bridge, Thailand
|
||||
|
||||
## 📈 **Grafana Visualization Examples**
|
||||
|
||||
### **1. Station Location Map**
|
||||
- **Type**: Geomap with markers
|
||||
- **Data**: Current station locations
|
||||
- **Color**: Water level or discharge percentage
|
||||
- **Size**: Discharge volume
|
||||
|
||||
### **2. Regional Water Levels**
|
||||
- **Type**: Geomap with heatmap
|
||||
- **Data**: Water level data across regions
|
||||
- **Visualization**: Color-coded intensity map
|
||||
- **Filters**: Time range, station groups
|
||||
|
||||
### **3. Alert Zones**
|
||||
- **Type**: Geomap with threshold markers
|
||||
- **Data**: Stations exceeding alert thresholds
|
||||
- **Visualization**: Red markers for high water levels
|
||||
- **Alerts**: Automated notifications for critical levels
|
||||
|
||||
## 🔄 **Updating a Running System**
|
||||
|
||||
### **Automated Migration Script**
|
||||
Use the provided migration script to safely add geolocation columns to your existing database:
|
||||
|
||||
```bash
|
||||
# Stop the water monitoring service first
|
||||
sudo systemctl stop water-monitor
|
||||
|
||||
# Run the migration script
|
||||
python migrate_geolocation.py
|
||||
|
||||
# Restart the service
|
||||
sudo systemctl start water-monitor
|
||||
```
|
||||
|
||||
### **Migration Script Features**
|
||||
- ✅ **Auto-detects database type** from environment variables
|
||||
- ✅ **Checks existing columns** to avoid conflicts
|
||||
- ✅ **Supports all database types** (SQLite, PostgreSQL, MySQL)
|
||||
- ✅ **Adds sample data** for P.1 station
|
||||
- ✅ **Safe operation** - won't break existing data
|
||||
|
||||
### **Step-by-Step Migration Process**
|
||||
|
||||
#### **1. Stop the Application**
|
||||
```bash
|
||||
# If running as systemd service
|
||||
sudo systemctl stop water-monitor
|
||||
|
||||
# If running in screen/tmux
|
||||
# Use Ctrl+C to stop the process
|
||||
|
||||
# If running as Docker container
|
||||
docker stop water-monitor
|
||||
```
|
||||
|
||||
#### **2. Backup Your Database**
|
||||
```bash
|
||||
# SQLite backup
|
||||
cp water_monitoring.db water_monitoring.db.backup
|
||||
|
||||
# PostgreSQL backup
|
||||
pg_dump water_monitoring > water_monitoring_backup.sql
|
||||
|
||||
# MySQL backup
|
||||
mysqldump water_monitoring > water_monitoring_backup.sql
|
||||
```
|
||||
|
||||
#### **3. Run Migration Script**
|
||||
```bash
|
||||
# Default (uses environment variables)
|
||||
python migrate_geolocation.py
|
||||
|
||||
# Or specify database path for SQLite
|
||||
SQLITE_DB_PATH=/path/to/water_monitoring.db python migrate_geolocation.py
|
||||
```
|
||||
|
||||
#### **4. Verify Migration**
|
||||
```bash
|
||||
# Check SQLite schema
|
||||
sqlite3 water_monitoring.db ".schema stations"
|
||||
|
||||
# Check PostgreSQL schema
|
||||
psql -d water_monitoring -c "\d stations"
|
||||
|
||||
# Check MySQL schema
|
||||
mysql -e "DESCRIBE water_monitoring.stations"
|
||||
```
|
||||
|
||||
#### **5. Update Application Code**
|
||||
Ensure you have the latest version of the application with geolocation support:
|
||||
```bash
|
||||
# Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# Install any new dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
#### **6. Restart Application**
|
||||
```bash
|
||||
# Systemd service
|
||||
sudo systemctl start water-monitor
|
||||
|
||||
# Docker container
|
||||
docker start water-monitor
|
||||
|
||||
# Manual execution
|
||||
python water_scraper_v3.py
|
||||
```
|
||||
|
||||
### **Migration Output Example**
|
||||
```
|
||||
2025-07-28 17:30:00,123 - INFO - Starting geolocation column migration...
|
||||
2025-07-28 17:30:00,124 - INFO - Detected database type: SQLITE
|
||||
2025-07-28 17:30:00,125 - INFO - Migrating SQLite database: water_monitoring.db
|
||||
2025-07-28 17:30:00,126 - INFO - Current columns in stations table: ['id', 'station_code', 'thai_name', 'english_name', 'created_at', 'updated_at']
|
||||
2025-07-28 17:30:00,127 - INFO - Added latitude column
|
||||
2025-07-28 17:30:00,128 - INFO - Added longitude column
|
||||
2025-07-28 17:30:00,129 - INFO - Added geohash column
|
||||
2025-07-28 17:30:00,130 - INFO - Successfully added columns: latitude, longitude, geohash
|
||||
2025-07-28 17:30:00,131 - INFO - Updated P.1 station with sample geolocation data
|
||||
2025-07-28 17:30:00,132 - INFO - P.1 station geolocation: ('P.1', 15.6944, 100.2028, 'w5q6uuhvfcfp25')
|
||||
2025-07-28 17:30:00,133 - INFO - ✅ Migration completed successfully!
|
||||
2025-07-28 17:30:00,134 - INFO - You can now restart your water monitoring application
|
||||
2025-07-28 17:30:00,135 - INFO - The system will automatically use the new geolocation columns
|
||||
```
|
||||
|
||||
## 🔍 **Troubleshooting**
|
||||
|
||||
### **Migration Issues**
|
||||
|
||||
#### **Database Locked Error**
|
||||
```bash
|
||||
# Stop all processes using the database
|
||||
sudo systemctl stop water-monitor
|
||||
pkill -f water_scraper
|
||||
|
||||
# Wait a few seconds, then run migration
|
||||
sleep 5
|
||||
python migrate_geolocation.py
|
||||
```
|
||||
|
||||
#### **Permission Denied**
|
||||
```bash
|
||||
# Check database file permissions
|
||||
ls -la water_monitoring.db
|
||||
|
||||
# Fix permissions if needed
|
||||
sudo chown $USER:$USER water_monitoring.db
|
||||
chmod 664 water_monitoring.db
|
||||
```
|
||||
|
||||
#### **Missing Dependencies**
|
||||
```bash
|
||||
# For PostgreSQL
|
||||
pip install psycopg2-binary
|
||||
|
||||
# For MySQL
|
||||
pip install pymysql
|
||||
|
||||
# For all databases
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### **Verification Issues**
|
||||
|
||||
#### **Missing Coordinates**
|
||||
If stations don't appear on the geomap:
|
||||
1. Check if latitude/longitude are NULL in database
|
||||
2. Verify geolocation data in station mapping
|
||||
3. Ensure database schema includes geolocation columns
|
||||
4. Run migration script if columns are missing
|
||||
|
||||
#### **Incorrect Positioning**
|
||||
If stations appear in wrong locations:
|
||||
1. Verify coordinate format (decimal degrees)
|
||||
2. Check latitude/longitude order (lat first, lon second)
|
||||
3. Validate geohash accuracy
|
||||
|
||||
### **Rollback Procedure**
|
||||
If migration causes issues:
|
||||
|
||||
#### **SQLite Rollback**
|
||||
```bash
|
||||
# Stop application
|
||||
sudo systemctl stop water-monitor
|
||||
|
||||
# Restore backup
|
||||
cp water_monitoring.db.backup water_monitoring.db
|
||||
|
||||
# Restart with old version
|
||||
sudo systemctl start water-monitor
|
||||
```
|
||||
|
||||
#### **PostgreSQL Rollback**
|
||||
```sql
|
||||
-- Remove added columns
|
||||
ALTER TABLE stations DROP COLUMN IF EXISTS latitude;
|
||||
ALTER TABLE stations DROP COLUMN IF EXISTS longitude;
|
||||
ALTER TABLE stations DROP COLUMN IF EXISTS geohash;
|
||||
```
|
||||
|
||||
#### **MySQL Rollback**
|
||||
```sql
|
||||
-- Remove added columns
|
||||
ALTER TABLE stations DROP COLUMN latitude;
|
||||
ALTER TABLE stations DROP COLUMN longitude;
|
||||
ALTER TABLE stations DROP COLUMN geohash;
|
||||
```
|
||||
|
||||
## 🎯 **Next Steps**
|
||||
|
||||
### **Immediate Actions**
|
||||
1. **Gather Coordinates**: Collect GPS coordinates for all 16 stations
|
||||
2. **Update Database**: Add coordinates to remaining stations
|
||||
3. **Create Dashboards**: Build Grafana geomap visualizations
|
||||
|
||||
### **Future Enhancements**
|
||||
1. **Automatic Geocoding**: API integration for address-to-coordinate conversion
|
||||
2. **Mobile GPS**: Mobile app for field coordinate collection
|
||||
3. **Satellite Integration**: Satellite imagery overlay in Grafana
|
||||
4. **Geofencing**: Alert zones based on geographic boundaries
|
||||
|
||||
The geolocation functionality is now fully implemented and ready for use with Grafana's geomap visualization. Station P.1 (Nawarat Bridge) serves as a working example with complete coordinate data.
|
||||
Reference in New Issue
Block a user