mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- 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.
186 lines
6.0 KiB
Go
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")
|
|
)
|