turash/bugulma/backend/internal/service/transportation_service.go
Damir Mukimov 0df4812c82
feat: Complete geographical features implementation with full test coverage
- Add comprehensive geographical data models (GeographicalFeature, TransportMode, TransportProfile, TransportOption)
- Implement geographical feature repository with PostGIS support and spatial queries
- Create transportation service for cost calculation and route optimization
- Build spatial resource matcher for geographical resource matching
- Develop environmental impact service for site environmental scoring
- Implement facility location optimizer with multi-criteria analysis
- Add geographical data migration service for SQLite to PostgreSQL migration
- Create database migrations for geographical features and site footprints
- Update geospatial service integration and server initialization
- Add CLI command for geographical data synchronization
- Implement complete test coverage for all geographical components (28 test cases)
- Update test infrastructure for geographical table creation and PostGIS handling

This implements advanced geospatial capabilities including transportation cost modeling, environmental impact assessment, and facility location optimization for the Turash platform.
2025-11-25 06:42:18 +01:00

186 lines
6.0 KiB
Go

package service
import (
"errors"
"fmt"
"math"
"sort"
"bugulma/backend/internal/domain"
"bugulma/backend/internal/geospatial"
)
// TransportationService handles transportation cost calculations and route optimization
type TransportationService struct {
geoCalc geospatial.Calculator
}
// NewTransportationService creates a new transportation service
func NewTransportationService(geoCalc geospatial.Calculator) *TransportationService {
return &TransportationService{
geoCalc: geoCalc,
}
}
// Default transport profiles (simplified for Bugulma region)
var transportProfiles = map[domain.TransportMode]domain.TransportProfile{
domain.TransportModeTruck: {
CostPerKm: 0.12, // €0.12 per km for truck transport
SpeedKmH: 60.0, // 60 km/h average speed
MaxCapacity: 25.0, // 25 tons
EnvironmentalFactor: 1.0, // Baseline
},
domain.TransportModeRail: {
CostPerKm: 0.08, // €0.08 per km (more efficient)
SpeedKmH: 40.0, // 40 km/h average speed
MaxCapacity: 100.0, // 100 tons
EnvironmentalFactor: 0.7, // Better for environment
},
domain.TransportModePipe: {
CostPerKm: 0.05, // €0.05 per km (fixed infrastructure)
SpeedKmH: 100.0, // 100 km/h (fluid transport)
MaxCapacity: 1000.0, // 1000 tons (continuous flow)
EnvironmentalFactor: 0.5, // Excellent for environment
},
}
// CalculateTransportCost calculates transportation cost between two points
func (t *TransportationService) CalculateTransportCost(
fromLat, fromLng, toLat, toLng float64,
mode domain.TransportMode,
volume float64,
) (*TransportCost, error) {
// Calculate distances
straightResult, err := t.geoCalc.CalculateDistance(
geospatial.Point{Latitude: fromLat, Longitude: fromLng},
geospatial.Point{Latitude: toLat, Longitude: toLng},
)
if err != nil {
return nil, err
}
// Estimate road distance (1.3x straight-line as approximation)
roadDistance := straightResult.DistanceKm * 1.3
profile, exists := transportProfiles[mode]
if !exists {
return nil, ErrInvalidTransportMode
}
// Check capacity
if volume > profile.MaxCapacity {
return nil, ErrVolumeExceedsCapacity
}
// Calculate costs
transportCost := roadDistance * profile.CostPerKm
timeToDeliver := (roadDistance / profile.SpeedKmH)
cost := &TransportCost{
TransportMode: mode,
StraightDistanceKm: straightResult.DistanceKm,
RoadDistanceKm: roadDistance,
CostEur: transportCost,
TimeHours: timeToDeliver,
EnvironmentalFactor: profile.EnvironmentalFactor,
CapacityUtilization: (volume / profile.MaxCapacity) * 100,
}
return cost, nil
}
// FindOptimalTransportRoutes finds the most cost-effective transportation routes
func (t *TransportationService) FindOptimalTransportRoutes(
fromLat, fromLng, toLat, toLng float64,
volume float64,
) ([]*domain.TransportOption, error) {
var options []*domain.TransportOption
for mode, profile := range transportProfiles {
// Check if the transport mode is feasible for this volume
if volume > profile.MaxCapacity {
continue
}
// Calculate distances
result, err := t.geoCalc.CalculateDistance(
geospatial.Point{Latitude: fromLat, Longitude: fromLng},
geospatial.Point{Latitude: toLat, Longitude: toLng},
)
if err != nil {
continue
}
// Estimate road distance
roadDistance := result.DistanceKm * 1.3
// Calculate costs
transportCost := roadDistance * profile.CostPerKm
timeToDeliver := (roadDistance / profile.SpeedKmH)
// Environmental score (higher is better)
environmentalScore := 10.0 / profile.EnvironmentalFactor
option := &domain.TransportOption{
TransportMode: mode,
DistanceKm: roadDistance,
CostEur: transportCost,
TimeHours: timeToDeliver,
EnvironmentalScore: environmentalScore,
CapacityUtilization: (volume / profile.MaxCapacity) * 100,
}
// Calculate overall efficiency score
option.OverallScore = t.calculateTransportEfficiency(option)
options = append(options, option)
}
// Sort by overall score (highest first)
sort.Slice(options, func(i, j int) bool {
return options[i].OverallScore > options[j].OverallScore
})
return options, nil
}
// GetTransportProfile returns the profile for a transport mode
func (t *TransportationService) GetTransportProfile(mode domain.TransportMode) (domain.TransportProfile, error) {
profile, exists := transportProfiles[mode]
if !exists {
return domain.TransportProfile{}, fmt.Errorf("invalid transport mode: %s", mode)
}
return profile, nil
}
// calculateTransportEfficiency computes an overall efficiency score for transport options
func (t *TransportationService) calculateTransportEfficiency(option *domain.TransportOption) float64 {
// Multi-criteria scoring: cost, time, environment, capacity utilization
costEfficiency := math.Max(0, 1.0-(option.CostEur/1000.0)) // Better under €1000
timeEfficiency := math.Max(0, 1.0-(option.TimeHours/24.0)) // Better under 24 hours
envEfficiency := option.EnvironmentalScore / 10.0 // 0-1 scale
capacityEfficiency := math.Min(option.CapacityUtilization/100.0, 1.0) // Optimal around 80-100%
// Weighted average
return (costEfficiency * 0.4) + (timeEfficiency * 0.3) + (envEfficiency * 0.2) + (capacityEfficiency * 0.1)
}
// TransportCost represents detailed transportation cost analysis
type TransportCost struct {
TransportMode domain.TransportMode `json:"transport_mode"`
StraightDistanceKm float64 `json:"straight_distance_km"`
RoadDistanceKm float64 `json:"road_distance_km"`
CostEur float64 `json:"cost_eur"`
TimeHours float64 `json:"time_hours"`
EnvironmentalFactor float64 `json:"environmental_factor"`
CapacityUtilization float64 `json:"capacity_utilization_percent"`
}
// Errors
var (
ErrInvalidTransportMode = errors.New("invalid transport mode specified")
ErrVolumeExceedsCapacity = errors.New("transport volume exceeds capacity")
)