mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
Repository Structure:
- Move files from cluttered root directory into organized structure
- Create archive/ for archived data and scraper results
- Create bugulma/ for the complete application (frontend + backend)
- Create data/ for sample datasets and reference materials
- Create docs/ for comprehensive documentation structure
- Create scripts/ for utility scripts and API tools
Backend Implementation:
- Implement 3 missing backend endpoints identified in gap analysis:
* GET /api/v1/organizations/{id}/matching/direct - Direct symbiosis matches
* GET /api/v1/users/me/organizations - User organizations
* POST /api/v1/proposals/{id}/status - Update proposal status
- Add complete proposal domain model, repository, and service layers
- Create database migration for proposals table
- Fix CLI server command registration issue
API Documentation:
- Add comprehensive proposals.md API documentation
- Update README.md with Users and Proposals API sections
- Document all request/response formats, error codes, and business rules
Code Quality:
- Follow existing Go backend architecture patterns
- Add proper error handling and validation
- Match frontend expected response schemas
- Maintain clean separation of concerns (handler -> service -> repository)
176 lines
4.9 KiB
Go
176 lines
4.9 KiB
Go
package geospatial
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// GeospatialCalculator implements the main Calculator interface
|
|
type GeospatialCalculator struct {
|
|
config *Config
|
|
distanceCalc DistanceCalculator
|
|
bearingCalc BearingCalculator
|
|
boundingBoxCalc BoundingBoxCalculator
|
|
coordinateTransformer CoordinateTransformer
|
|
validator SpatialValidator
|
|
}
|
|
|
|
// NewGeospatialCalculator creates a new geospatial calculator with all dependencies
|
|
func NewGeospatialCalculator(
|
|
config *Config,
|
|
distanceCalc DistanceCalculator,
|
|
bearingCalc BearingCalculator,
|
|
boundingBoxCalc BoundingBoxCalculator,
|
|
coordinateTransformer CoordinateTransformer,
|
|
validator SpatialValidator,
|
|
) Calculator {
|
|
return &GeospatialCalculator{
|
|
config: config,
|
|
distanceCalc: distanceCalc,
|
|
bearingCalc: bearingCalc,
|
|
boundingBoxCalc: boundingBoxCalc,
|
|
coordinateTransformer: coordinateTransformer,
|
|
validator: validator,
|
|
}
|
|
}
|
|
|
|
// CalculateDistance calculates distance between two points
|
|
func (gc *GeospatialCalculator) CalculateDistance(p1, p2 Point) (DistanceResult, error) {
|
|
var distanceKm float64
|
|
var err error
|
|
|
|
switch gc.config.DefaultDistanceMethod {
|
|
case "vincenty":
|
|
if vincentyCalc, ok := gc.distanceCalc.(*DistanceCalculatorImpl); ok {
|
|
distanceKm, err = vincentyCalc.VincentyDistance(p1, p2)
|
|
} else {
|
|
distanceKm, err = gc.distanceCalc.HaversineDistance(p1, p2)
|
|
}
|
|
default:
|
|
distanceKm, err = gc.distanceCalc.HaversineDistance(p1, p2)
|
|
}
|
|
|
|
if err != nil {
|
|
return DistanceResult{}, err
|
|
}
|
|
|
|
bearingDeg, err := gc.bearingCalc.CalculateBearing(p1, p2)
|
|
if err != nil {
|
|
return DistanceResult{}, err
|
|
}
|
|
|
|
return DistanceResult{
|
|
DistanceKm: distanceKm,
|
|
DistanceMeters: distanceKm * 1000,
|
|
Bearing: bearingDeg,
|
|
BearingRadians: bearingDeg * 3.14159265359 / 180,
|
|
}, nil
|
|
}
|
|
|
|
// CalculateDistanceMatrix calculates distances between multiple points
|
|
func (gc *GeospatialCalculator) CalculateDistanceMatrix(points []Point) (*DistanceMatrix, error) {
|
|
if len(points) == 0 {
|
|
return nil, ErrEmptyPointList
|
|
}
|
|
|
|
if len(points) > gc.config.MaxDistanceMatrixSize {
|
|
return nil, ErrMatrixTooLarge
|
|
}
|
|
|
|
matrix := make([][]float64, len(points))
|
|
for i := range matrix {
|
|
matrix[i] = make([]float64, len(points))
|
|
}
|
|
|
|
for i := 0; i < len(points); i++ {
|
|
for j := 0; j < len(points); j++ {
|
|
if i == j {
|
|
matrix[i][j] = 0
|
|
} else {
|
|
distance, err := gc.distanceCalc.HaversineDistance(points[i], points[j])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to calculate distance between points %d and %d: %w", i, j, err)
|
|
}
|
|
matrix[i][j] = distance
|
|
}
|
|
}
|
|
}
|
|
|
|
return &DistanceMatrix{
|
|
Points: points,
|
|
Distances: matrix,
|
|
}, nil
|
|
}
|
|
|
|
// CalculateBearing calculates bearing between two points
|
|
func (gc *GeospatialCalculator) CalculateBearing(from, to Point) (float64, error) {
|
|
return gc.bearingCalc.CalculateBearing(from, to)
|
|
}
|
|
|
|
// CalculateBoundingBox calculates bounding box for points
|
|
func (gc *GeospatialCalculator) CalculateBoundingBox(points []Point) (*BoundingBox, error) {
|
|
return gc.boundingBoxCalc.CalculateBoundingBox(points)
|
|
}
|
|
|
|
// IsPointInBoundingBox checks if point is in bounding box
|
|
func (gc *GeospatialCalculator) IsPointInBoundingBox(point Point, bbox BoundingBox) bool {
|
|
return gc.boundingBoxCalc.IsPointInBoundingBox(point, bbox)
|
|
}
|
|
|
|
// CalculateRoute calculates a route through multiple points
|
|
func (gc *GeospatialCalculator) CalculateRoute(points []Point) (*Route, error) {
|
|
if len(points) < 2 {
|
|
return nil, ErrInvalidRoute
|
|
}
|
|
|
|
var totalDistance float64
|
|
var segments []RouteSegment
|
|
|
|
for i := 0; i < len(points)-1; i++ {
|
|
distanceResult, err := gc.CalculateDistance(points[i], points[i+1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to calculate segment %d: %w", i, err)
|
|
}
|
|
|
|
totalDistance += distanceResult.DistanceKm
|
|
|
|
// Estimate time if average speed is configured
|
|
var timeMinutes *int
|
|
if gc.config.AverageSpeedKmh > 0 {
|
|
minutes := int((distanceResult.DistanceKm / gc.config.AverageSpeedKmh) * 60)
|
|
timeMinutes = &minutes
|
|
}
|
|
|
|
segments = append(segments, RouteSegment{
|
|
From: points[i],
|
|
To: points[i+1],
|
|
DistanceKm: distanceResult.DistanceKm,
|
|
Bearing: distanceResult.Bearing,
|
|
TimeMinutes: timeMinutes,
|
|
})
|
|
}
|
|
|
|
// Calculate total time
|
|
var totalTimeMinutes *int
|
|
if gc.config.AverageSpeedKmh > 0 {
|
|
totalMinutes := int((totalDistance / gc.config.AverageSpeedKmh) * 60)
|
|
totalTimeMinutes = &totalMinutes
|
|
}
|
|
|
|
return &Route{
|
|
Points: points,
|
|
TotalDistanceKm: totalDistance,
|
|
Segments: segments,
|
|
EstimatedTimeMinutes: totalTimeMinutes,
|
|
}, nil
|
|
}
|
|
|
|
// ValidatePoint validates a point
|
|
func (gc *GeospatialCalculator) ValidatePoint(point Point) error {
|
|
return gc.validator.ValidatePoint(point)
|
|
}
|
|
|
|
// ValidateBoundingBox validates a bounding box
|
|
func (gc *GeospatialCalculator) ValidateBoundingBox(bbox BoundingBox) error {
|
|
return gc.validator.ValidateBoundingBox(bbox)
|
|
}
|