Contributing to Transport & Logistics
The Transport & Logistics module is a comprehensive transportation service providing route planning, public transport information, and travel cost estimation for Ghana. This guide will help you contribute to transport-related features.
π Current Transport Featuresβ
Implemented Featuresβ
- Transport Stops - Bus stops, stations, and public transport hubs
- Route Calculation - Optimal routing between locations with multiple transport modes
- Route Directions - Detailed turn-by-turn navigation instructions
- Travel Cost Estimation - Fuel costs and fare calculations for different transport modes
- Fuel Prices - Current petrol, diesel, and LPG prices in Ghana
- Nearby Services - Find transport stops within a specified radius
Feature Areas for Contributionβ
π Public Transport Enhancementβ
- Real-time bus arrival information
- Transport schedule integration
- Route frequency and timing data
- Multi-modal journey planning
- Accessibility information for transport stops
πΊοΈ Route Optimizationβ
- Traffic-aware routing
- Alternative route suggestions
- Time-based routing (rush hour optimization)
- Commercial vehicle routing restrictions
- Eco-friendly route options
π° Cost & Pricingβ
- Dynamic pricing based on demand
- Historical price trend analysis
- Cost comparison between transport modes
- Fuel efficiency optimization
- Carbon footprint calculations
π± Data Integrationβ
- Integration with ride-sharing services
- Real-time traffic data
- Weather impact on routes
- Road closure and construction updates
- Emergency route alternatives
π Geographic Expansionβ
- Additional city coverage
- Rural area transport options
- Inter-city transport connections
- Cross-border transport information
π οΈ Technical Architectureβ
File Structureβ
backend/src/transport/
βββ transport.controller.ts # API route handlers
βββ transport.service.ts # Main coordinator service
βββ transport.module.ts # NestJS module configuration
βββ index.ts # Central exports
βββ services/ # Specialized service modules
β βββ geocoding.service.ts # Location name to coordinates conversion
β βββ fuel-price.service.ts # Fuel price data retrieval
β βββ routing.service.ts # Route calculation and directions
β βββ transport-routes.service.ts # Public transport route management
β βββ transport-stops.service.ts # Transport stops and stations
βββ dto/ # Data Transfer Objects
β βββ route-directions.dto.ts # Route directions request/response
β βββ route-calculation.dto.ts # Route calculation models
β βββ transport-stop.dto.ts # Transport stop data models
β βββ fuel-price.dto.ts # Fuel pricing models
β βββ travel-cost.dto.ts # Travel cost estimation
β βββ nearby-stops.dto.ts # Nearby stops search
β βββ error-response.dto.ts # Error handling models
βββ entities/ # Database entities
βββ providers/ # External service providers
Service Architectureβ
The transport module follows a modular service architecture where the main TransportService
acts as a coordinator that delegates to specialized services:
Main TransportService (Coordinator)β
// transport.service.ts
@Injectable()
export class TransportService {
constructor(
private readonly geocodingService: GeocodingService,
private readonly fuelPriceService: FuelPriceService,
private readonly transportRoutesService: TransportRoutesService,
private readonly transportStopsService: TransportStopsService,
private readonly routingService: RoutingService
) {}
// Main business logic and coordination
async getRouteDirections(
query: RouteDirectionsQueryDto
): Promise<RouteDirectionsResponseDto>;
// Input validation and orchestration
// Combines results from multiple services
}
Specialized Servicesβ
GeocodingServiceβ
// services/geocoding.service.ts
@Injectable()
export class GeocodingService {
// Convert place names to coordinates
async geocodeLocation(locationName: string): Promise<GeocodingResult>;
// Providers: Nominatim, Overpass API
}
FuelPriceServiceβ
// services/fuel-price.service.ts
@Injectable()
export class FuelPriceService {
// Get current fuel prices in Ghana from major companies
async getFuelPrices(): Promise<FuelPrice>;
// Provider: CediRates.com web scraper
// Calculates averages from Shell, Goil, Total, and Star Oil
}
RoutingServiceβ
// services/routing.service.ts
@Injectable()
export class RoutingService {
// Calculate routes between coordinates
async calculateRoute(start: [number, number], end: [number, number], mode: string): Promise<RouteCalculation>
async getRouteWithFallback(...): Promise<RouteResult>
// Utility methods
validateGhanaCoordinates(lat: number, lng: number): void
calculateHaversineDistance(...): number
// Providers: OpenRouteService, HERE API, GraphHopper
}
TransportRoutesServiceβ
// services/transport-routes.service.ts
@Injectable()
export class TransportRoutesService {
// Manage public transport routes
async getPublicTransportRoutes(city: string): Promise<TransportRoute[]>;
// Providers: Overpass API, GTFS, HERE API
}
TransportStopsServiceβ
// services/transport-stops.service.ts
@Injectable()
export class TransportStopsService {
// Manage transport stops and stations
async getTransportStops(city: string, ...): Promise<TransportStop[]>
// Providers: Overpass API, GTFS
}
External API Integrationβ
Each specialized service manages its own set of provider APIs with fallback mechanisms:
// GeocodingService - Location Resolution
private readonly geocodingApis = [
'nominatim', // OpenStreetMap Nominatim
'overpass' // Overpass API
];
// RoutingService - Route Calculation
private readonly routingApis = [
'openrouteservice', // Open source routing
'here', // HERE Maps API
'graphhopper' # GraphHopper API
];
// TransportRoutesService & TransportStopsService - Public Transport Data
private readonly transportApis = [
'overpass', // OpenStreetMap Overpass API
'gtfs', // General Transit Feed Specification
'here' // HERE Transit API
];
// FuelPriceService - Fuel Price Data
private readonly fuelPriceApis = [
'cedirates-scraper', // CediRates.com web scraper
];
Benefits of Service Separationβ
- Separation of Concerns: Each service handles a specific domain
- Independent Testing: Services can be unit tested in isolation
- Maintainability: Easier to update individual API integrations
- Reusability: Services can be used independently across the application
- Scalability: Individual services can be optimized separately
- Error Isolation: Failures in one service don't affect others
Using Services Independentlyβ
Services can be imported and used independently in other parts of the application:
// Using individual services
import { GeocodingService, FuelPriceService } from "../transport/services";
@Injectable()
export class SomeOtherService {
constructor(
private readonly geocodingService: GeocodingService,
private readonly fuelPriceService: FuelPriceService
) {}
async getSomeData() {
const location = await this.geocodingService.geocodeLocation("Accra");
const prices = await this.fuelPriceService.getFuelPrices();
// Use data independently
}
}
// Using the main coordinating service
import { TransportService } from "../transport";
@Injectable()
export class AnotherService {
constructor(private readonly transportService: TransportService) {}
async getRouteInfo() {
// Uses all services through the main coordinator
return this.transportService.getRouteDirections(query);
}
}
π§ Development Setupβ
Prerequisitesβ
- Node.js 18+ and npm
- PostgreSQL (for data storage)
- Redis (for caching)
- Access to external API keys (optional but recommended)
Environment Variablesβ
# External API Keys (optional)
OPENROUTESERVICE_API_KEY=your_ors_key
HERE_API_KEY=your_here_key
GRAPHHOPPER_API_KEY=your_graphhopper_key
# Database
POSTGRES_URL=postgresql://user:password@localhost:5432/ghanaapi
REDIS_URL=redis://localhost:6379
# Fuel price sources
# No additional configuration needed - uses CediRates.com web scraping
Local Developmentβ
# Navigate to backend
cd backend/
# Install dependencies
npm install
# Start development server
npm run start:dev
# Run tests
npm run test:e2e -- --testPathPattern=transport
π§ͺ Testingβ
Unit Testsβ
Testing Individual Servicesβ
// geocoding.service.spec.ts
describe("GeocodingService", () => {
it("should geocode Accra correctly", async () => {
const result = await geocodingService.geocodeLocation("Accra");
expect(result.coordinates[0]).toBeCloseTo(5.6037, 1);
expect(result.coordinates[1]).toBeCloseTo(-0.187, 1);
});
});
// fuel-price.service.spec.ts
describe("FuelPriceService", () => {
it("should return fuel prices averaged from major companies", async () => {
const prices = await fuelPriceService.getFuelPrices();
expect(prices.petrol).toBeGreaterThan(0);
expect(prices.diesel).toBeGreaterThan(0);
expect(prices.currency).toBe("GHS");
expect(prices.source).toContain("Shell, Goil, Total, Star Oil");
});
});
// routing.service.spec.ts
describe("RoutingService", () => {
it("should calculate route between valid coordinates", async () => {
const result = await routingService.calculateRoute(
[5.6037, -0.187], // Accra
[6.6885, -1.6244], // Kumasi
"driving"
);
expect(result.distance).toBeGreaterThan(200000); // > 200km
expect(result.duration).toBeGreaterThan(0);
expect(result.coordinates).toHaveLength.greaterThan(2);
});
});
Testing Main TransportServiceβ
// transport.service.spec.ts
describe("TransportService", () => {
let service: TransportService;
let geocodingService: jest.Mocked<GeocodingService>;
let routingService: jest.Mocked<RoutingService>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
TransportService,
{ provide: GeocodingService, useValue: mockGeocodingService },
{ provide: RoutingService, useValue: mockRoutingService },
// ... other mocked services
],
}).compile();
service = module.get<TransportService>(TransportService);
geocodingService = module.get(GeocodingService);
routingService = module.get(RoutingService);
});
describe("getRouteDirections", () => {
it("should coordinate services to provide route directions", async () => {
// Test that main service properly coordinates sub-services
const query = { start_name: "Accra", end_name: "Kumasi" };
geocodingService.geocodeLocation
.mockResolvedValueOnce({ coordinates: [5.6037, -0.187] })
.mockResolvedValueOnce({ coordinates: [6.6885, -1.6244] });
routingService.getRouteWithFallback.mockResolvedValue({
distance: 250000,
duration: 10800,
});
const result = await service.getRouteDirections(query);
expect(result.distance).toBe(250000);
expect(geocodingService.geocodeLocation).toHaveBeenCalledTimes(2);
expect(routingService.getRouteWithFallback).toHaveBeenCalledTimes(1);
});
});
});
Integration Testsβ
// transport.controller.e2e-spec.ts
describe("Transport Controller (e2e)", () => {
it("/transport/stops (GET)", () => {
return request(app.getHttpServer())
.get("/transport/stops?city=accra")
.expect(200)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.data).toBeInstanceOf(Array);
expect(res.body.count).toBeGreaterThan(0);
});
});
});
π Data Sourcesβ
Transport Stop Dataβ
- OpenStreetMap (Overpass API) - Public transport stops and routes
- GTFS Feeds - Official transit agency data
- HERE Transit API - Commercial transit information
- Community Contributions - Crowdsourced updates
Routing Dataβ
- OpenRouteService - Open-source routing engine
- HERE Maps - Commercial mapping service
- GraphHopper - Fast routing service
- OpenStreetMap - Community-maintained road network
Fuel Price Dataβ
- CediRates.com - Real-time scraping from major Ghanaian fuel companies
- Direct Company Data - Shell, Goil, Total, and Star Oil price aggregation
π― Contribution Guidelinesβ
1. Data Quality Standardsβ
Transport Stop Requirementsβ
interface TransportStop {
id: string; // Unique identifier
name: string; // Human-readable name
coordinates: [number, number]; // [lat, lng] in WGS84
type: "bus_stop" | "station" | "platform" | "taxi_stand";
routes?: string[]; // Associated route IDs
amenities?: string[]; // Available facilities
accessibility?: {
wheelchair: boolean;
visual_aid: boolean;
audio_aid: boolean;
};
}
Route Validationβ
- All coordinates must be within Ghana boundaries (4.5Β°N to 11.5Β°N, 3.5Β°W to 1.5Β°E)
- Routes should follow actual road networks
- Distance calculations must be accurate within 5% margin
- Duration estimates should account for traffic patterns
2. API Response Standardsβ
Success Response Formatβ
{
"success": true,
"data": {
// Actual response data
},
"count": 10, // For array responses
"metadata": {
"provider": "openrouteservice",
"timestamp": "2024-01-15T10:30:00Z"
}
}
Error Response Formatβ
{
"success": false,
"message": "Failed to calculate route",
"error": "Coordinates are outside Ghana boundaries",
"timestamp": "2024-01-15T10:30:00Z"
}
3. Performance Requirementsβ
Response Time Targetsβ
- Route Calculation - < 2 seconds
- Transport Stops - < 500ms
- Nearby Search - < 1 second
- Fuel Prices - < 50ms (cached) / < 2 seconds (fresh)
Caching Strategyβ
- Route calculations - 1 hour cache
- Transport stops - 24 hour cache
- Fuel prices - Daily cache (expires at 11:59 PM)
- Static data - 7 day cache
4. Error Handlingβ
Graceful Degradationβ
async getRouteDirections(query: RouteDirectionsQueryDto) {
for (const provider of this.routingApis) {
try {
return await this.getDirectionsFromProvider(provider, query);
} catch (error) {
this.logger.warn(`Provider ${provider} failed: ${error.message}`);
continue;
}
}
throw new HttpException(
'All routing services are unavailable',
HttpStatus.SERVICE_UNAVAILABLE
);
}
Input Validationβ
- Coordinate bounds checking
- Parameter type validation
- Enum value validation
- Required field validation
π Contributing Workflowβ
1. Setting Up Your Environmentβ
# Fork the repository
git clone https://github.com/yourusername/GhanaAPI.git
cd GhanaAPI
# Create feature branch
git checkout -b feature/transport-enhancement
# Set up backend environment
cd backend/
npm install
cp .env.example .env
# Configure your environment variables
# Run tests to ensure everything works
npm run test
2. Making Changesβ
Working with the Service Architectureβ
When contributing to the transport module, you'll typically work with one or more of the specialized services:
Contributing to GeocodingServiceβ
// services/geocoding.service.ts
// Examples: Adding new geocoding providers, improving accuracy
export class GeocodingService {
async geocodeLocation(locationName: string): Promise<GeocodingResult> {
// Add new geocoding provider
// Improve existing geocoding logic
// Add location name normalization
}
}
Contributing to FuelPriceServiceβ
// services/fuel-price.service.ts
// Examples: Improving CediRates scraping, adding company price validation
export class FuelPriceService {
async getFuelPrices(): Promise<FuelPrice> {
// Improve CediRates.com HTML parsing
// Add price validation for major companies
// Implement fallback data strategies
// Add historical price tracking
}
}
Contributing to RoutingServiceβ
// services/routing.service.ts
// Examples: Adding new routing providers, optimization algorithms
export class RoutingService {
async calculateRoute(start, end, mode): Promise<RouteCalculation> {
// Add new routing provider
// Implement traffic-aware routing
// Add route optimization features
}
}
Contributing to TransportRoutesServiceβ
// services/transport-routes.service.ts
// Examples: Adding new route data sources, real-time updates
export class TransportRoutesService {
async getPublicTransportRoutes(city: string): Promise<TransportRoute[]> {
// Add GTFS feed support
// Implement real-time route data
// Add route scheduling information
}
}
Contributing to TransportStopsServiceβ
// services/transport-stops.service.ts
// Examples: Enhancing stop data, adding amenity information
export class TransportStopsService {
async getTransportStops(city, ...): Promise<TransportStop[]> {
// Add stop accessibility information
// Implement nearby facilities data
// Add real-time stop information
}
}
Adding New Transport Stopsβ
- Update the transport stop data files
- Add validation for new stop types
- Update DTOs and interfaces
- Add unit tests
- Update documentation
Implementing New Routing Featuresβ
- Add new method to
TransportService
- Create appropriate DTOs
- Add controller endpoint
- Implement error handling
- Add API documentation annotations
- Write comprehensive tests
Adding New Data Sourcesβ
- Create new provider service
- Implement fallback logic
- Add configuration options
- Update caching strategy
- Add monitoring and logging
3. Code Review Checklistβ
Functionalityβ
- Feature works as specified
- Error handling is comprehensive
- Performance meets requirements
- Caching is properly implemented
Code Qualityβ
- TypeScript types are properly defined
- Code follows existing patterns
- Comments explain complex logic
- No hardcoded values
Testingβ
- Unit tests cover new functionality
- Integration tests pass
- Edge cases are tested
- Error scenarios are tested
Documentationβ
- API documentation is updated
- Code comments are clear
- README updates if needed
- Contributing guide updates
4. Submitting Your Contributionβ
# Commit your changes
git add .
git commit -m "feat(transport): add real-time bus tracking"
# Push to your fork
git push origin feature/transport-enhancement
# Create pull request on GitHub
# Include:
# - Clear description of changes
# - Screenshots/demos if applicable
# - Testing instructions
# - Breaking changes (if any)
π Monitoring and Analyticsβ
Key Metrics to Trackβ
- Response Times - API endpoint performance
- Success Rates - Request success/failure ratios
- Data Accuracy - Route distance/duration accuracy
- Provider Reliability - External API uptime
- Usage Patterns - Most requested routes and features
Logging Guidelinesβ
// Service-level logging
this.logger.log(`Calculating route from ${start} to ${end} using ${provider}`);
this.logger.warn(`Provider ${provider} returned invalid data`);
this.logger.error(`Failed to calculate route: ${error.message}`, error.stack);
// Performance logging
const startTime = Date.now();
const result = await this.calculateRoute(start, end, mode);
this.logger.debug(`Route calculation took ${Date.now() - startTime}ms`);
π Security Considerationsβ
Input Sanitizationβ
- Validate all coordinate inputs
- Sanitize place name queries
- Prevent injection attacks
- Rate limiting on expensive operations
API Key Managementβ
- Store API keys securely
- Rotate keys regularly
- Monitor usage quotas
- Implement fallback providers
Data Privacyβ
- Don't log user coordinates
- Anonymize usage analytics
- Comply with data protection regulations
- Secure data transmission
π Localization and Accessibilityβ
Language Supportβ
- Place names in local languages
- Multi-language turn-by-turn directions
- Local transport terminology
- Cultural context awareness
Accessibility Featuresβ
- Wheelchair-accessible route options
- Audio navigation support
- Visual impairment considerations
- Public transport accessibility information
π Resourcesβ
Ghana Transportation Contextβ
- Ghana Highway Code - Official driving regulations
- Metropolitan Transport Strategy - Urban planning documents
- Road Network Atlas - Infrastructure mapping
- Public Transport Studies - Academic research
Technical Documentationβ
- OpenStreetMap Ghana - Community mapping data
- National Road Fund - Infrastructure data
- Ghana Statistical Service - Transportation statistics
- Ministry of Transport - Policy documents
Development Resourcesβ
- OpenRouteService Docs - API documentation
- HERE Developer Portal - Commercial API guides
- GraphHopper Documentation - Routing engine docs
- GTFS Specification - Transit data format
π‘ Ideas for New Contributorsβ
Easy Contributionsβ
Service-Specific Easy Tasksβ
- GeocodingService: Add support for local language place names
- FuelPriceService: Improve error handling for web scraping failures
- TransportStopsService: Add validation for stop data quality
- TransportRoutesService: Add new transport route types (ferry, metro)
- RoutingService: Add input validation for coordinate bounds
General Easy Tasksβ
- Add new transport stops for specific cities
- Improve error messages and user feedback
- Update fuel price calculation formulas
- Add unit tests for existing functionality
Intermediate Contributionsβ
Service-Specific Intermediate Tasksβ
- GeocodingService: Implement caching for geocoding results
- FuelPriceService: Add historical price tracking and trends
- RoutingService: Implement traffic-aware routing
- TransportStopsService: Add real-time stop information
- TransportRoutesService: Add route scheduling and frequency data
General Intermediate Tasksβ
- Implement caching for expensive operations
- Add support for new transport modes
- Improve route optimization algorithms
- Create data validation tools
Advanced Contributionsβ
Service-Specific Advanced Tasksβ
- GeocodingService: Machine learning for improved geocoding accuracy
- FuelPriceService: Predictive fuel price modeling based on company trends
- RoutingService: Multi-modal journey planning optimization
- TransportStopsService: Real-time crowd density at stops
- TransportRoutesService: Dynamic route optimization based on traffic
General Advanced Tasksβ
- Real-time traffic integration
- Machine learning for route prediction
- Mobile app API optimization
- Multi-modal journey planning
π Getting Helpβ
Communication Channelsβ
- GitHub Issues - Bug reports and feature requests
- GitHub Discussions - General questions and ideas
- Pull Request Reviews - Code feedback and suggestions
Common Issues and Solutionsβ
External API Failuresβ
- Check API key validity
- Verify rate limits
- Test with fallback providers
- Review error logs
Route Calculation Errorsβ
- Validate coordinate boundaries
- Check transport mode support
- Verify road network connectivity
- Test with known working routes
Performance Issuesβ
- Review caching implementation
- Check database query performance
- Monitor external API response times
- Optimize data processing
Thank you for contributing to GhanaAPI's Transport & Logistics module! Your contributions help make transportation information more accessible to developers and users across Ghana.
For general contribution guidelines, see Contributing Overview