component decomposition is done

This commit is contained in:
Oleksandr Bezdieniezhnykh
2025-11-24 14:09:23 +02:00
parent acec83018b
commit f50006d100
34 changed files with 8637 additions and 0 deletions
@@ -0,0 +1,294 @@
# Waypoint Validator
## Interface Definition
**Interface Name**: `IWaypointValidator`
### Interface Methods
```python
class IWaypointValidator(ABC):
@abstractmethod
def validate_waypoint(self, waypoint: Waypoint) -> ValidationResult:
pass
@abstractmethod
def validate_geofence(self, geofence: Geofences) -> ValidationResult:
pass
@abstractmethod
def check_bounds(self, waypoint: Waypoint, geofences: Geofences) -> bool:
pass
@abstractmethod
def validate_route_continuity(self, waypoints: List[Waypoint]) -> ValidationResult:
pass
```
## Component Description
### Responsibilities
- Validate individual waypoint data (GPS coordinates, altitude, confidence)
- Validate geofence definitions (polygon bounds, topology)
- Check waypoints against geofence boundaries
- Validate route continuity (detect large gaps, validate sequencing)
- Provide detailed validation error messages
### Scope
- Input validation for Route API
- Business rule enforcement (operational area restrictions for Ukraine)
- Geospatial boundary checking
- Data quality assurance
## API Methods
### `validate_waypoint(waypoint: Waypoint) -> ValidationResult`
**Description**: Validates a single waypoint's data integrity and constraints.
**Called By**:
- R01 Route REST API (before creating/updating)
- R02 Route Data Manager (pre-persistence validation)
**Input**:
```python
Waypoint:
id: str
lat: float
lon: float
altitude: Optional[float]
confidence: float
timestamp: datetime
refined: bool
```
**Output**:
```python
ValidationResult:
valid: bool
errors: List[str]
```
**Validation Rules**:
1. **Latitude**: -90.0 <= lat <= 90.0
2. **Longitude**: -180.0 <= lon <= 180.0
3. **Altitude**: 0 <= altitude <= 1000 meters (if provided)
4. **Confidence**: 0.0 <= confidence <= 1.0
5. **Timestamp**: Not in future, not older than 1 year
6. **Operational area** (Ukraine restriction): Latitude ~45-52N, Longitude ~22-40E
7. **ID**: Non-empty string
**Error Conditions**:
- Returns `ValidationResult` with `valid=False` and error list (not an exception)
**Test Cases**:
1. **Valid waypoint**: All fields correct → returns `valid=True`
2. **Invalid latitude**: lat=100 → returns `valid=False`, error="Latitude out of range"
3. **Invalid longitude**: lon=200 → returns `valid=False`
4. **Invalid confidence**: confidence=1.5 → returns `valid=False`
5. **Future timestamp**: timestamp=tomorrow → returns `valid=False`
6. **Outside operational area**: lat=10 (not Ukraine) → returns `valid=False`
7. **Valid altitude**: altitude=500 → returns `valid=True`
8. **Invalid altitude**: altitude=1500 → returns `valid=False`
---
### `validate_geofence(geofence: Geofences) -> ValidationResult`
**Description**: Validates geofence polygon definitions.
**Called By**:
- R01 Route REST API (during route creation)
**Input**:
```python
Geofences:
polygons: List[Polygon]
Polygon:
north_west: GPSPoint
south_east: GPSPoint
```
**Output**:
```python
ValidationResult:
valid: bool
errors: List[str]
```
**Validation Rules**:
1. **North-West corner**: NW.lat > SE.lat
2. **North-West corner**: NW.lon < SE.lon (for Eastern Ukraine)
3. **Polygon size**: Max 500km × 500km
4. **Polygon count**: 1 <= len(polygons) <= 10
5. **No self-intersection**: Polygons should not overlap
6. **Within operational area**: All corners within Ukraine bounds
**Error Conditions**:
- Returns `ValidationResult` with validation errors
**Test Cases**:
1. **Valid geofence**: Single polygon in Ukraine → valid=True
2. **Invalid corners**: NW.lat < SE.lat → valid=False
3. **Too large**: 600km × 600km → valid=False
4. **Too many polygons**: 15 polygons → valid=False
5. **Overlapping polygons**: Two overlapping → valid=False (warning)
---
### `check_bounds(waypoint: Waypoint, geofences: Geofences) -> bool`
**Description**: Checks if a waypoint falls within geofence boundaries.
**Called By**:
- R01 Route REST API (optional check during waypoint updates)
- R02 Route Data Manager (business rule enforcement)
**Input**:
```python
waypoint: Waypoint
geofences: Geofences
```
**Output**:
```python
bool: True if waypoint is within any geofence polygon
```
**Algorithm**:
- Point-in-polygon test for each geofence polygon
- Returns True if point is inside at least one polygon
**Error Conditions**:
- None (returns False if outside all geofences)
**Test Cases**:
1. **Inside geofence**: Waypoint in polygon center → returns True
2. **Outside geofence**: Waypoint 10km outside → returns False
3. **On boundary**: Waypoint on polygon edge → returns True
4. **Multiple geofences**: Waypoint in second polygon → returns True
---
### `validate_route_continuity(waypoints: List[Waypoint]) -> ValidationResult`
**Description**: Validates route continuity, detecting large gaps and sequence issues.
**Called By**:
- R01 Route REST API (during route creation)
- R02 Route Data Manager (route quality check)
**Input**:
```python
waypoints: List[Waypoint] # Should be ordered by sequence/timestamp
```
**Output**:
```python
ValidationResult:
valid: bool
errors: List[str]
warnings: List[str]
```
**Validation Rules**:
1. **Minimum waypoints**: len(waypoints) >= 2
2. **Maximum waypoints**: len(waypoints) <= 3000
3. **Timestamp ordering**: waypoints[i].timestamp < waypoints[i+1].timestamp
4. **Distance gaps**: Consecutive waypoints < 500 meters apart (warning if violated)
5. **Large gap detection**: Flag gaps > 1km (warning for potential data loss)
6. **No duplicate timestamps**: All timestamps unique
**Error Conditions**:
- Returns `ValidationResult` with errors and warnings
**Test Cases**:
1. **Valid route**: 100 waypoints, 100m spacing → valid=True
2. **Too few waypoints**: 1 waypoint → valid=False
3. **Too many waypoints**: 3500 waypoints → valid=False
4. **Unordered timestamps**: waypoints out of order → valid=False
5. **Large gap**: 2km gap between waypoints → valid=True with warning
6. **Duplicate timestamps**: Two waypoints same time → valid=False
## Integration Tests
### Test 1: Complete Validation Pipeline
1. Create waypoint with all valid data
2. validate_waypoint() → passes
3. Create geofence for Eastern Ukraine
4. validate_geofence() → passes
5. check_bounds() → waypoint inside geofence
### Test 2: Route Validation Flow
1. Create 500 waypoints with 100m spacing
2. validate_route_continuity() → passes
3. Add waypoint 2km away
4. validate_route_continuity() → passes with warning
5. Add waypoint with past timestamp
6. validate_route_continuity() → fails
### Test 3: Edge Cases
1. Waypoint on geofence boundary
2. Waypoint at North Pole (lat=90)
3. Waypoint at dateline (lon=180)
4. Route with exactly 3000 waypoints
## Non-Functional Requirements
### Performance
- **validate_waypoint**: < 1ms per waypoint
- **validate_geofence**: < 10ms per geofence
- **check_bounds**: < 2ms per check
- **validate_route_continuity**: < 100ms for 2000 waypoints
### Accuracy
- GPS coordinate validation: 6 decimal places precision (0.1m)
- Geofence boundary check: 1-meter precision
### Maintainability
- Validation rules configurable via configuration file
- Easy to add new validation rules
- Clear error messages for debugging
## Dependencies
### Internal Components
- **R04 Route Database Layer**: For loading existing route data (optional context)
- **H06 Web Mercator Utils**: For distance calculations (optional)
### External Dependencies
- **Shapely** (optional): For advanced polygon operations
- **Geopy**: For geodesic distance calculations
## Data Models
### ValidationResult
```python
class ValidationResult(BaseModel):
valid: bool
errors: List[str] = []
warnings: List[str] = []
```
### OperationalArea (Configuration)
```python
class OperationalArea(BaseModel):
name: str = "Eastern Ukraine"
min_lat: float = 45.0
max_lat: float = 52.0
min_lon: float = 22.0
max_lon: float = 40.0
```
### ValidationRules (Configuration)
```python
class ValidationRules(BaseModel):
max_altitude: float = 1000.0 # meters
max_waypoint_gap: float = 500.0 # meters
max_route_waypoints: int = 3000
min_route_waypoints: int = 2
max_geofence_size: float = 500000.0 # meters (500km)
max_geofences: int = 10
```