Real-time maritime vessel tracking system with AI-powered assistance and intelligent port destination decoding
Live Demo • API Documentation • Video Demo (Under Development) • Report Bug
- Overview
- Key Features
- The Problem We Solve
- System Architecture
- Technology Stack
- Data Flow
- Getting Started
- Project Structure
- API Endpoints
- Deployment
- Contributing
- License
- Acknowledgments
Live Ship Vessel Tracker is a comprehensive full-stack maritime tracking application that provides real-time vessel monitoring, intelligent destination decoding, and AI-powered maritime assistance. Built with modern web technologies and designed for scalability, this system demonstrates advanced data processing, API integration, and real-time data visualization.
- Intelligent AIS Destination Decoding - Transforms messy, unpredictable AIS destination codes into clean, readable port names and countries
- Batch Processing - Track up to 20 vessels simultaneously with a single request
- AI-Powered Chatbot (COMPASS) - Get instant information about vessels, ports, and maritime routes using Google's Gemini AI
- Real-Time Caching - Redis-powered caching system reduces API calls by 80-90% and improves response times
- Interactive Mapping - Mapbox integration with custom markers for vessels and destination ports
-
Single Vessel Tracking
- Search by IMO number
- View current position on interactive map
- See detailed vessel specifications
- Track destination port with coordinates
- Real-time navigation status
-
Batch Vessel Search
- Process up to 20 vessels simultaneously
- Bulk data retrieval and normalization
- Aggregated results with success/failure tracking
- Export capabilities for data analysis
-
Smart Destination Decoding
- Handles 15+ AIS destination formats
- UN/LOCODE to readable port names
- Multi-format parsing (
BEZEE<>GBHUL,SGSIN=>BRPMA, etc.) - Fuzzy matching with 98% accuracy
- Country and port coordinate resolution
- COMPASS AI Assistant
- Context-aware conversations about vessels
- Maritime knowledge database
- Vessel specification queries
- Port information and routing advice
- Powered by Google Gemini AI
-
Redis Caching Layer
- 7-day TTL (Time To Live)
- 80-90% cache hit rate
- Sub-50ms response times for cached data
- Automatic cache invalidation
-
Efficient Data Processing
- Batch processing with rate limiting
- Parallel API calls
- Optimized database queries
- Lazy loading for improved UX
- Interactive Maps (Mapbox GL)
- Dark theme optimized for maritime data
- Custom vessel markers
- Destination port indicators
- Auto-fit bounds
- Zoom and navigation controls
- Popup information on hover
AIS (Automatic Identification System) destination data is notoriously inconsistent and difficult to interpret. Vessels report their destinations in various unpredictable formats:
❌ Raw AIS Data Problems:
"BEZEE <> GBHUL" → What does this mean?
"SGSIN=>BRPMA" → Which ports are these?
"LYBEN>>MTMAR" → Arrows? Really?
"TR IST" → UN/LOCODE format
"PORT SAID" → Full name (ambiguous)
"TBA" → To Be Announced (no info)
"GIBRALTAR EAST ANCH" → Port + Anchorage area
✅ Our System Transforms:
"BEZEE <> GBHUL" → Zeebrugge, Belgium → Hull, United Kingdom
"SGSIN=>BRPMA" → Singapore → Paranaguá, Brazil
"LYBEN>>MTMAR" → Benghazi, Libya → Marsa, Malta
"TR IST" → Istanbul, Turkey (41.0082°N, 28.9784°E)
"PORT SAID" → Port Said, Egypt (31.2565°N, 32.2841°E)
How did I Do It:
- Pattern Recognition - 15+ format parsers for different AIS conventions
- UN/LOCODE Matching - Database of 100,000+ maritime locations
- Fuzzy Matching - 98% accuracy with intelligent similarity algorithms
- Route Extraction - Identifies final destination from multi-leg routes
- Geocoding - Provides precise coordinates for map visualization
┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ (React + Mapbox + Tailwind) │
└───────────────────────────┬─────────────────────────────────────┘
│
┌───────┴────────┐
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Express.js │ │ Flask Backend │
│ Backend API │ │ (AI-Model) │
├────────────────┤ ├────────────────┤
│ • Vessel Data │ │ • AIS Decoder │
│ • Normalization│---->│ • Gemini AI │
│ • Map Data │ │ • Port Matcher │
│ • Redis Cache │ │ • UN/LOCODE DB │
└────────┬───────┘ └────────┬───────┘
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Redis Cache │ │ locode.json │
│ (7-day TTL) │ │ (Port Database)│
└────────────────┘ └────────────────┘
│
▼
┌────────────────┐
│ AIS Friends │
│ API Endpoint │
│ (Discovered) │
└────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ Request Flow │
└──────────────────────────────────────────────────────────────────┘
1. User Input (IMO: 9626390)
│
├─→ Frontend React Component
│ └─→ API Service (axios)
│
2. Express Backend Receives Request
│
├─→ Check Redis Cache
│ ├─→ HIT: Return cached data (50ms)
│ └─→ MISS: Continue to API
│
3. Fetch from AIS Friends API
│ └─→ Raw vessel data retrieved
│
4. Data Normalization
│
├─→ Normalize vessel name & type
│
├─→ Send AIS destination to Flask
│ │
│ ├─→ Flask Port Matcher
│ │ ├─→ Pattern extraction
│ │ ├─→ UN/LOCODE matching
│ │ ├─→ Fuzzy matching
│ │ └─→ Coordinate resolution
│ │
│ └─→ Return: {port, country, lat, lon}
│
├─→ Generate Mapbox markers
│
└─→ Store in Redis (7-day TTL)
│
5. Return to Frontend
│
└─→ Display: Map + Details + Table
User Initializes Chatbot
│
└─→ Flask: Connects to Gemini API and initiates messaging
- React 18.3.1 - UI framework with hooks
- React Router 6 - Client-side routing
- Tailwind CSS v4 - Utility-first styling
- Mapbox GL JS - Interactive maps
- Axios - HTTP client
- Lucide React - Icon library
- Vite - Build tool & dev server
- Node.js 18+ - Runtime environment
- Express.js 4.18 - Web framework
- Redis 4.6 - Caching layer
- node-fetch 3.3 - HTTP requests
- dotenv - Environment management
- CORS - Cross-origin resource sharing
- Python 3.10+ - Runtime
- Flask 3.0 - Micro framework
- Flask-CORS - CORS handling
- Google Generative AI - Gemini integration
- Gunicorn - WSGI server
- Custom Port Matcher - UN/LOCODE algorithm
- Redis - In-memory data store
- Netlify - Frontend hosting
- Render - Backend hosting (Flask + Express)
- Mapbox - Map tiles and geocoding
- AIS Friends API - Vessel data source
- Google Gemini - AI chatbot
- Ollama Mistral - Local AI Model (testing prompt engineering)
- Jest - Testing framework
- ESLint - Code linting
- Git - Version control
- GitHub - Repository Storage
sequenceDiagram
participant User
participant Frontend
participant Express
participant Redis
participant AIS API
participant Flask
participant Gemini
User->>Frontend: Search IMO: 9626390
Frontend->>Express: GET /api/vessel/9626390
Express->>Redis: Check cache
alt Cache Hit
Redis-->>Express: Return cached data
Express-->>Frontend: Vessel data + map
else Cache Miss
Express->>AIS API: Fetch vessel data
AIS API-->>Express: Raw vessel data
Express->>Flask: POST /api/destination {dest: "TR IST"}
Flask->>Flask: UN/LOCODE matching
Flask-->>Express: {port: "Istanbul", country: "Turkey"}
Express->>Express: Generate map data
Express->>Redis: Store (7-day TTL)
Express-->>Frontend: Vessel data + map
end
Frontend->>User: Display map + details
User->>Frontend: Click chatbot
Frontend->>Flask: POST /api/chat/init
Flask->>Gemini: Initialize session
Gemini-->>Flask: Greeting message
Flask-->>Frontend: AI response
Frontend->>User: Show chat interface
User Input: [9626390, 9377418, 7349106]
│
▼
┌─────────────────────────────────────┐
│ Express: Check Redis for all IMOs │
├─────────────────────────────────────┤
│ Found in cache: [9626390] │ ← 1/3 cached
│ Need to fetch: [9377418, 7349106] │ ← 2/3 fetch
└─────────────────┬───────────────────┘
│
┌─────────┴─────────┐
│ │
▼ ▼
[Cached] [Fetch from API]
9626390 9377418, 7349106
│ │
│ ┌────┴────┐
│ │ │
│ Normalize Normalize
│ 9377418 7349106
│ │ │
│ ┌────-────┐
│ ▼ ▼
│ Flask API Flask API
│ │ │
│ ▼ ▼
│ Store Store
│ Cache Cache
│ │ │
└─────────-─────────-────┐
▼
┌────────────────────────────┐
│ Combine all 3 vessels │
│ Generate batch map data │
│ Return to frontend │
└────────────────────────────┘
Before you begin, ensure you have the following installed:
-
Node.js (v18.0.0 or higher)
node --version # Should be >= 18.0.0 -
npm (comes with Node.js)
npm --version
-
Python (v3.10 or higher)
python --version # Should be >= 3.10 -
Redis (v6.0 or higher)
redis-server --version
-
Git
git --version
git clone https://github.com/ryantusi/GMS_Vessel_Tracker.git
cd vessel-trackercd backend
# Install dependencies
npm install
# Create environment file
cp .env.example .env
# Edit .env with your credentials
# Required variables:
# - DESTINATION_DECODER_API (Flask URL)
# - MAPBOX_ACCESS_TOKEN (from Mapbox)
# - REDIS_URL (Redis connection string)Backend .env file:
# Flask API
DESTINATION_DECODER_API=http://127.0.0.1:10000/api/destination
AI_CHATBOT_API=http://127.0.0.1:10000/api/chat
# Server Configuration
PORT=5000
NODE_ENV=development
# Mapbox Configuration
MAPBOX_ACCESS_TOKEN=your_mapbox_token_here
MAPBOX_STYLE=mapbox://styles/your_style_here
# Redis Cache
REDIS_URL=redis://localhost:6379
# CORS
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173cd ../AI-Model
# Create virtual environment
python -m venv venv
# Activate virtual environment
# On Windows:
venv\Scripts\activate
# On Mac/Linux:
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Create environment file
cp .env.example .env
# Edit .env with your Gemini API keyFlask .env file:
PORT=10000
GEMINI_API_KEY=your_gemini_api_key_hereGet Gemini API Key:
- Go to https://makersuite.google.com/app/apikey
- Create new API key
- Copy and paste into
.env
cd ../frontend
# Install dependencies
npm install
# Create environment file
cp .env.example .env
# Edit .env with API URLsFrontend .env file:
# Backend API URL
VITE_API_URL=http://localhost:5000
# Mapbox Token
VITE_MAPBOX_TOKEN=your_mapbox_token_here
# Flask Backend
VITE_FLASK_API_URL=http://127.0.0.1:10000Get Mapbox Token:
- Go to https://account.mapbox.com/
- Create account (free)
- Copy your default public token
- Or create a new token with appropriate scopes
# On Windows (if installed via MSI):
redis-server
# On Mac (with Homebrew):
brew services start redis
# On Linux:
sudo systemctl start redis-server
# Or using Docker:
docker run -d --name redis -p 6379:6379 redis:latestVerify Redis is running:
redis-cli ping
# Should return: PONGOpen three separate terminal windows:
cd AI-Model
source venv/bin/activate # On Windows: venv\Scripts\activate
python app.pyExpected output:
✅ Mock database loaded: 20 vessels
* Running on http://127.0.0.1:10000
cd backend
npm startExpected output:
✅ Redis connected successfully
🚢 Ship Tracker API running on port 5000
💾 Redis cache: ENABLED
cd frontend
npm run devExpected output:
VITE v5.4.10 ready in 543 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
Open your browser and navigate to:
http://localhost:5173
You should see:
- ✅ Disclaimer modal (explaining demo mode)
- ✅ Home page with search forms
- ✅ Interactive map centered on Suez Canal
- ✅ No console errors
- Enter IMO:
9626390 - Click "Search Vessel"
- Should display vessel details and map
- Enter wrong IMO:
9711819 - Should display a clean error message
- Enter IMOs:
99626390, 9377418, 7349106, 9711819 (wrong imo) - Click "Search Multiple Vessels"
- Should display results table
- Click the floating chat button (bottom-right)
- Chat panel opens
- Type: "What is the status of vessel 9626390?"
- COMPASS responds with vessel information
vessel-tracker/
├── AI-Model/ # Flask backend for AI & destination decoding
│ ├── chatbot/ # Gemini AI chatbot implementation
│ │ ├── chatbot.py # Main chatbot logic
│ │ └── testbot.py # Ollama test version
│ ├── helper/ # Utility functions
│ │ ├── ais_port_matcher.py # UN/LOCODE matching algorithm
│ │ ├── script.py # Database generation script
│ │ ├── code-list.csv # UN/LOCODE port codes
│ │ ├── country-codes.csv # Country reference data
│ │ └── locode.json # Generated port database (100K+ entries)
│ ├── app.py # Flask application & routes
│ ├── requirements.txt # Python dependencies
│ ├── mock-vessels.json # Mock vessel database (deployment)
│ └── .env # Environment variables
│
├── backend/ # Express.js backend API
│ ├── utils/ # Utility modules
│ │ ├── apiData.js # AIS data fetching
│ │ ├── normalizer.js # Data normalization
│ │ ├── mapbox.js # Map data generation
│ │ └── cache.js # Redis caching service
│ ├── tests/ # Unit tests
│ │ ├── server.test.js # API endpoint tests
│ │ └── setup.js # Test configuration
│ ├── server.js # Express server & routes
│ ├── package.json # Node dependencies
│ ├── jest.config.js # Jest configuration
│ ├── mock-vessels.json # Mock data (deployment)
│ └── .env # Environment variables
│
├── frontend/ # React frontend application
│ ├── src/
│ │ ├── components/ # React components
│ │ │ ├── Chatbot.jsx # AI chatbot interface
│ │ │ ├── DisclaimerModal.jsx # Demo disclaimer
│ │ │ ├── ErrorMessage.jsx # Error handling
│ │ │ ├── Footer.jsx # Page footer
│ │ │ ├── LoadingSpinner.jsx # Loading states
│ │ │ └── MapComponent.jsx # Mapbox integration
│ │ ├── pages/ # Page components
│ │ │ ├── Home.jsx # Landing page
│ │ │ ├── SingleVessel.jsx # Vessel details
│ │ │ └── BatchVessels.jsx # Batch results
│ │ ├── services/ # API services
│ │ │ └── api.js # Axios API client
│ │ ├── App.jsx # Root component
│ │ ├── main.jsx # React entry point
│ │ └── index.css # Global styles
│ ├── public/ # Static assets
│ ├── package.json # Node dependencies
│ ├── vite.config.js # Vite configuration
│ ├── tailwind.config.js # Tailwind CSS config
│ └── .env # Environment variables
│
├── database/ # Mock database (deployment)
│ └── mock-vessels.json # 20 vessels for demo
│
├── docs/ # Documentation
│ └── setup-guides/ # Setup instructions
│
├── .gitignore # Git ignore rules
├── LICENSE # MIT License
└── README.md # This file
GET /Returns API information and available endpoints.
GET /api/vessel/:imoParameters:
imo- IMO number (7-10 digits)
Response:
{
"success": true,
"vessel": {
"imo": "9626390",
"name": "Ruby",
"type": "mv",
"ais_destination": {
"destination": "Istanbul, Turkey",
"port": "Istanbul",
"country": "Turkey",
"lat": 41.0082,
"lon": 28.9784
},
"latitude": 41.125858,
"longitude": 29.078135,
"navigational_status": "Underway using engine",
"speed_over_ground": 10.0,
"reportedDestination": "TR IST"
},
"map": {
"markers": [...],
"bounds": [[lng1, lat1], [lng2, lat2]],
"center": [lng, lat]
},
"cached": false
}POST /api/vessels/batch
Content-Type: application/json
{
"imos": ["9626390", "9377418", "7349106"]
}Response:
{
"success": true,
"totalRequested": 3,
"totalSuccess": 3,
"totalFailed": 0,
"cachedCount": 1,
"fetchedCount": 2,
"vessels": [...],
"map": {...}
}GET /api/cache/statsDELETE /api/cache/vessel/:imoGET /POST /api/destination
Content-Type: application/json
{
"destination": "TR IST"
}Response:
{
"reportedDestination": "TR IST",
"locode": "TRIST",
"port": "Istanbul",
"country": "Turkey",
"lat": 41.0082,
"lon": 28.9784,
"matched": true
}GET /api/chat/initPOST /api/chat
Content-Type: application/json
{
"message": "What is the status of vessel 9626390?"
}- Frontend: Netlify
- Express Backend: Render
- Flask Backend: Render
See detailed deployment guide: DEPLOYMENT.md
Summary:
- Deploy Flask → Get URL
- Deploy Express → Update with Flask URL
- Deploy Frontend → Update with both backend URLs
- Update CORS settings
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit changes (
git commit -m 'Add AmazingFeature') - Push to branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the CC0 1.0 Universal - see the LICENSE file for details.
- AIS Friends API - For providing accessible AIS vessel data (and for blocking my hosting priviledges everywhere)
- UN/LOCODE - Maritime location code system
- Mapbox - Interactive mapping platform
- Google Gemini - AI language model
- Ollama Mistral - AI Model for prompt engineering
- Open Source Community - For amazing tools and libraries
Ryan Tusi - LinkedIn
Portfolio: Click
AI-Model Flask | Express Backend | React Frontend
Built for the maritime industry. Engineered and Developed by Ryan Tusi, Full Stack + AI/ML Engineer
⚓️ 🚢 🌊