A real-time GPS tracking system for fleet management built with .NET Framework 4.6, ASP.NET Web API, Entity Framework 6, and vanilla JavaScript with Leaflet maps.
- Overview
- Features
- Prerequisites
- Installation & Setup
- Running the Application
- API Endpoints
- Configuration
- Database Schema
- Architectural & Modeling Decisions
- Assumptions Made
- What I Would Improve With More Time
- Troubleshooting
- Documentation Structure]
This system simulates and tracks GPS-equipped vehicles in real-time, similar to IoT and fleet management solutions. Vehicles send periodic location data, which is stored and visualized on an interactive map. Users can view vehicle positions, inspect historical routes, and calculate traveled distances.
- Real-time tracking of 100+ vehicles
- Route visualization with distance calculations
- Automated data generation for testing and demo purposes
- Single Page Application with no page reloads
- RESTful API following best practices
-
✅ Vehicle Grid View
- Display all vehicles with current status (active/inactive)
- Show last known position and timestamp
- Auto-refresh every 30 seconds (configurable)
- Click vehicle to view details
-
✅ Live Map
- Display all vehicle positions with color-coded markers
- Green markers: Active vehicles
- Red markers: Inactive vehicles
- Interactive popups with vehicle info
-
✅ Vehicle Detail Modal
- DateTime range filter
- Route visualization (blue polyline connecting GPS points)
- Start marker (green) and end marker (red)
- Calculate total traveled distance (Haversine formula)
- Display data points count and time range
-
✅ GPS Data Ingestion
- Single position submission
- Batch position submission (optimized)
- Multi-layer Validation:
- Domain: Coordinate range validation (lat: [-90, 90], lon: [-180, 180])
- Application: Vehicle existence, active status, geographic bounds (Athens)
- Infrastructure: Duplicate detection
- Business rules: Inactive vehicles reject new positions
-
✅ Data Querying
- List all vehicles
- Get vehicle with last known position
- Get GPS positions by time range
- Calculate route distance (Haversine formula)
-
✅ Service Layer
GpsService- GPS data ingestion and queryingVehicleService- Vehicle managementRouteCalculationService- Distance calculations (separated for SRP)GeographicalService- Haversine distance, bounding box checks
- ✅ Automated GPS Generation
- Generates realistic movement within 50m radius
- Configurable interval (default: 30 seconds)
- Configurable positions per vehicle (default: 5)
- Ensures positions stay within Athens bounding box
- Polly retry policies with exponential backoff (2s, 4s, 8s)
- Graceful error handling and detailed logging
- Parallel processing for multiple vehicles
- ✅ 25+ Unit Tests with NUnit + Moq
- Domain: Coordinates validation, value object behavior (8 tests)
- Application: RouteCalculationService, business logic (9 tests)
- Infrastructure: Validation rules (Strategy Pattern) (6+ tests)
- Test coverage: Domain invariants, edge cases, mocking
-
Visual Studio 2019 or 2022
- Workloads: .NET desktop development, ASP.NET and web development
- Download VS 2022 Community (Free)
-
SQL Server 2019+ Express Edition (or LocalDB)
- Download SQL Server Express
- Or use LocalDB (included with Visual Studio)
-
.NET Framework 4.6.1 Developer Pack
-
Git (for cloning the repository)
- SQL Server Management Studio (SSMS) - For database inspection
- Postman - For API testing
git clone https://github.com/anastasiosm/VehicleTrackingSystem.git
cd VehicleTrackingSystem# Open the solution file
VehicleTrackingSystem.slnOr from Visual Studio:
- File → Open → Project/Solution
- Navigate to
VehicleTrackingSystem.sln
Visual Studio should automatically restore packages. If not:
- Right-click Solution → Restore NuGet Packages
- Or use Package Manager Console:
Update-Package -reinstall
Edit: src\VehicleTracking.Web\Web.config
<connectionStrings>
<add name="VehicleTrackingDb"
connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=VehicleTrackingDb;Integrated Security=True;MultipleActiveResultSets=True"
providerName="System.Data.SqlClient" />
</connectionStrings><connectionStrings>
<add name="VehicleTrackingDb"
connectionString="Data Source=(LocalDB)\MSSQLLocalDB;Initial Catalog=VehicleTrackingDb;Integrated Security=True;MultipleActiveResultSets=True"
providerName="System.Data.SqlClient" />
</connectionStrings>The database will be created automatically on first run, including:
- Database schema (Vehicles, GpsPositions tables)
- Seed data (100 vehicles)
- Sample GPS positions
No manual migration needed! Just run the Web API (see next section).
Build → Rebuild Solution
Expected result:
========== Rebuild All: 5 succeeded, 0 failed, 0 skipped ==========
- Set VehicleTracking.Web as startup project (Right-click → Set as Startup Project)
- Press F5 (Debug) or Ctrl+F5 (Run without debugging)
- Browser will open at:
http://localhost:5000
What happens:
- Database creates automatically (if first run)
- 100 vehicles are seeded
- Web API starts
- Frontend (index.html) loads in browser
Verify API is working:
# Open these URLs in browser
http://localhost:5000/api/vehicles
http://localhost:5000/api/vehicles/with-last-positionsYou should see JSON data! ✅
While Web API is running, start the console app:
Method A: Multiple Startup Projects
- Right-click Solution → Properties
- Select Multiple startup projects
- Set both to Start:
- VehicleTracking.Web: Start
- VehicleTracking.DataGenerator: Start
- Click OK
- Press F5 - Both projects start together! 🎉
Method B: Manual Start
- Keep Web API running
- Right-click VehicleTracking.DataGenerator → Debug → Start New Instance
Console Output:
==============================================
Vehicle Tracking - GPS Data Generator
==============================================
Configuration:
API Base URL: http://localhost:5000
Interval: 30 seconds
Positions per vehicle: 5
Movement radius: 50 meters
Press CTRL+C to stop the generator...
[14:30:15] Starting iteration #1...
✓ Generated positions for 100 vehicles
✓ Total positions submitted: 500
✓ Failed submissions: 0
Next iteration in 30 seconds...
Get all vehicles with basic info.
Response:
{
"success": true,
"data": [
{
"id": 1,
"name": "VAN-001",
"isActive": true,
"lastPositionTimestamp": "2026-01-06T14:30:00Z"
}
]
}Get all vehicles with their last known GPS position (optimized for map display).
Submit a single GPS position.
Request:
{
"vehicleId": 1,
"latitude": 37.9838,
"longitude": 23.7275,
"recordedAt": "2026-01-06T14:30:00Z"
}Validations:
- Vehicle must exist and be active
- Coordinates must be within Athens bounding box (37.9-38.1, 23.6-23.8)
- No duplicate (VehicleId, RecordedAt) pairs
Submit multiple positions for a vehicle (recommended for performance).
Request:
{
"vehicleId": 1,
"positions": [
{
"latitude": 37.9838,
"longitude": 23.7275,
"recordedAt": "2026-01-06T14:30:00Z"
}
]
}Get vehicle route with calculated distance.
Response:
{
"success": true,
"data": {
"vehicleId": 1,
"vehicleName": "VAN-001",
"totalDistanceMeters": 1234.56,
"positionCount": 50
}
}File: src\VehicleTracking.Web\Web.config
<connectionStrings>
<add name="VehicleTrackingDb" connectionString="..." />
</connectionStrings>File: src\VehicleTracking.DataGenerator\App.config
<appSettings>
<add key="ApiBaseUrl" value="http://localhost:5000" />
<add key="IntervalSeconds" value="30" />
<add key="PositionsPerVehicle" value="5" />
<add key="RadiusMeters" value="50" />
</appSettings>Customization Examples:
- Faster updates:
IntervalSeconds = 10 - More positions:
PositionsPerVehicle = 10 - Larger movement:
RadiusMeters = 100
CREATE TABLE Vehicles (
Id INT PRIMARY KEY IDENTITY,
Name NVARCHAR(100) NOT NULL,
IsActive BIT NOT NULL,
CreatedDate DATETIME NOT NULL
);CREATE TABLE GpsPositions (
Id BIGINT PRIMARY KEY IDENTITY,
VehicleId INT NOT NULL FOREIGN KEY REFERENCES Vehicles(Id),
Latitude FLOAT NOT NULL,
Longitude FLOAT NOT NULL,
RecordedAt DATETIME NOT NULL,
CONSTRAINT UQ_Vehicle_RecordedAt UNIQUE (VehicleId, RecordedAt)
);
-- Indexes
CREATE INDEX IX_VehicleId ON GpsPositions(VehicleId);
CREATE INDEX IX_RecordedAt ON GpsPositions(RecordedAt);- 100 vehicles (VAN-001 to BUS-100)
- 50 sample GPS positions (first 10 vehicles)
Separated into Domain, Application, Infrastructure, Persistence, Web layers for testability and maintainability.
Separate validation rules (VehicleExists, VehicleActive, GeographicBounds, DuplicateDetection) for Open/Closed Principle.
Coordinates as readonly struct with validation enforces domain invariants.
Abstracted data access behind interfaces for testability.
Reduces HTTP overhead by submitting multiple positions in one request.
Domain exceptions for business rules, Application exceptions for use case failures.
Exponential backoff (2s, 4s, 8s) for transient HTTP failures.
- Athens-only - Hardcoded bounding box (37.9-38.1°N, 23.6-23.8°E)
- UTC timestamps - No timezone complexity
- No authentication - Demo environment
- Single vehicle type - No Car/Truck/Bus differentiation
- Polling over WebSockets - Simpler implementation
- In-memory caching - No Redis needed for demo
- LocalDB sufficient - Not production scale
- Integration tests with real database
- SignalR for real-time updates
- Frontend enhancements (search, dark mode, CSV export)
- JWT authentication & RBAC
- Redis distributed cache
- Application Insights monitoring
- Multi-region support
- Event Sourcing for audit trail
- CQRS + MediatR (when 20+ use cases)
- Microservices architecture
- Docker + Kubernetes
- Advanced ML features (predictive routing, anomaly detection)
Problem: Database connection errors
Solution: Verify SQL Server is running and connection string is correct in Web.config
Problem: API returns 500 errors
Solution: Check src\VehicleTracking.Web\App_Data\logs\web.log for detailed error messages
Problem: Data Generator fails to connect
Solution: Ensure Web API is running first and ApiBaseUrl in App.config is correct
Problem: Tests not appearing in Test Explorer
Solution: Clean & Rebuild solution, restart Visual Studio
Edit Web.config:
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add name="textWriterTraceListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="C:\logs\app.log" />
</listeners>
</trace>
</system.diagnostics>For more detailed technical information, please refer to the following documents in the /src/docs folder:
- DATABASE.md: Details on SQL schema, Entity Relationship Diagram, and EF6 mapping configurations.
- BACKEND_API.md: Complete list of REST endpoints, DTO structures, and architectural layering rules.
- GPS_GENERATOR.md: In-depth look at the High Performance Simulator, including parallel processing logic and movement algorithms.
- FRONTEND.md: Overview of the Single Page Application, Leaflet.js integration, and real-time polling implementation.
Enjoy tracking! 🚗💨