Opis
Komponent niewidoczny z palety „Maps” – middleware jak TAircraft/TWeather, ale dla ruchu morskiego. Domyślny serwer https://boatsea.imetroll.com (proxy AIS Digitraffic + sample, auth key=). Mapa z BoatsInfo=True rysuje ruchome ikony statków (wbudowane SVG lub własne). Klik → TMapInfoWindow + przycisk Pokaż trasę → /v1/tracks + okno z wykresem prędkości. Alias typu: TShips = TBoats.
Moduł: MapCommon.Boats (+ Boats.Types / Client / Overlay) · Klasa bazowa: TComponent · Platformy: VCL + FMX

Demo na żywo
Live AIS (boatsea.imetroll.com + key=): Active + prostokąt/koło/współrzędne/area= → states; klik → info → Pokaż trasę → chart SOG.
Zaznacz prostokąt / koło, wpisz lat, lon[, km] albo ApplyAreaId (gdansk, finland-gulf…) → GET boatsea /v1/states; klik → info → Pokaż trasę / SOG.
▶ Active + obszar / współrzędne / area=Funkcje
- Middleware: Request/RefreshAsync → GET
/v1/states+key=→ OnDataReady(JSON) / setBoats. - Obszar wymagany (jak fly): prostokąt
lamin…lomax, kołolat/lon/radius_kmlubarea=gdansk. Brak → HTTP 400; nigdy cały świat. - Active = auto query: zaznaczenie obszaru na mapie (prostokąt / koło) lub
ApplyCoords(Lat, Lon[, RadiusKm])→ od razuRefreshAsync. - Auth: RequireApiKey=True (domyślnie); bez ApiKey → bekUnauthorized; serwer 401 bez key=.
- Klasy statków z AIS: kontenerowiec, masowiec, tankowiec, chemikaliowiec, gazowiec, pasażerski, rybacki, okręt wojenny, straż, holownik, pilot, żaglowiec… → ikona + kolor + etykieta (tabela).
- Ikony: wg klasy (Display.TypeIcons, boatsea /static/ships/) albo wbudowane SVG / Display.IconDataURL (obrót po heading); Display.ClassFilter.
- Map.Boats / Boats.Map AutoLink; Map.BoatsInfo steruje rysowaniem.
- InfoWindow: klik statku → Title/Body; przycisk Pokaż trasę → ShowRouteAsync.
- Trasa:
/v1/tracks?mmsi=→ polyline na mapie + okno small/large z wykresem SOG (kn). - Provider bpBoatseaProxy (live) / bpSample (demo).
https://boatsea.imetroll.com (nginx + ships_proxy → Digitraffic AIS, fallback sample Bałtyk/PL).TAircraft / fly.imetroll.com.Właściwości
| Właściwość | Opis |
|---|---|
| ApiKey / Key, RequireApiKey, ApiKeyParam | Jak TWeather/TAircraft – key= na query. |
| BaseURL | Domyślnie https://boatsea.imetroll.com |
| Area.Kind / South|West|North|East | bakRectangle → lamin/lomin/lamax/lomax (domyślnie Bałtyk PL). |
| Area.CenterLat/Lon + RadiusKm | bakRadius → lat/lon/radius_km (domyślnie Zatoka Gdańska 50 km). |
| Area.AreaId | Nazwany obszar boatsea (baltic, gdansk, szczecin, finland-gulf…) → area=. |
| UseMapAreaSelection, AutoRequestOnAreaSelect, ShowAreaOnMap | Zaznaczenie mapy / współrzędne → Area; Active=True → RefreshAsync. |
| IntervalSec, Active | Timer; Active=True = auto-zapytanie po zmianie obszaru. |
| Display.* | Visible, IconDataURL, IconSize, ShowLabels, LabelField (blfShipClass), Opacity, AnimateMoves, TypeIcons, TypeIconBaseURL, ClassFilter. |
| Map, InfoWindow, AutoLinkComponents | Powiązania. |
| RouteWindowSize, RouteColor | Okno trasy small/large + kolor linii. |
| Provider | bpBoatseaProxy / bpSample. |
Zdarzenia
| Zdarzenie | Opis |
|---|---|
| OnDataReady(JSON) | Po udanym states. |
| OnBoatsClick(State) | Klik ikony statku. |
| OnRouteReady(Track, RouteJSON) | Po pobraniu trasy. |
| OnAreaApplied | Area podmienione z mapy / współrzędnych. |
| OnError | bekUnauthorized / bekEmpty (brak obszaru) / sieć / HTTP / parse. |
Metody
| Metoda | Opis |
|---|---|
| Request / Refresh / RefreshAsync | Pobranie stanów (zawsze dla Area). |
| BeginAreaSelect / CancelAreaSelect | Rysowanie prostokąta / koła na mapie (pan-lock). |
| ApplyMapArea / ApplyCoords / ApplyCoordsText / ApplyBounds / ApplyAreaId | Obszar z mapy / współrzędnych; Active → RefreshAsync. |
| ShowRoute / ShowRouteAsync / ClearRoute | Trasa + wykres SOG. |
| UpdateDisplay / ClearDisplay | Wypchnięcie setBoats. |
| DetectLinkedComponents | Map + InfoWindow. |
Przykłady
TBoats + mapa + InfoWindow + key=
Boats1.ApiKey := GetEnvironmentVariable('BOATS_API_KEY'); // &key=
Boats1.RequireApiKey := True;
Boats1.BaseURL := 'https://boatsea.imetroll.com';
Boats1.Area.South := 53.5; Boats1.Area.West := 14; // or Kind := bakRadius / AreaId := 'gdansk'
Boats1.Area.North := 55.5; Boats1.Area.East := 20.5;
Boats1.IntervalSec := 10;
TileMap1.Boats := Boats1;
TileMap1.BoatsInfo := True;
Boats1.InfoWindow := MapInfoWindow1; // klik → Okno/Dymek
Boats1.OnBoatsClick := BoatsClick;
Boats1.OnRouteReady := BoatsRouteReady;
Boats1.Active := True; // lub Boats1.Request;
// Active: zaznaczenie na mapie / współrzędne → zapytanie od razu
Boats1.BeginAreaSelect; // lub BeginAreaSelect(bakRadius)
Boats1.ApplyCoords(54.52, 18.65, 30); // lat/lon/radius_km
Active: BeginAreaSelect / ApplyCoords / ApplyAreaId
// Active = True: zaznaczenie / współrzędne / ApplyAreaId → RefreshAsync od razu
Boats1.ApiKey := GetEnvironmentVariable('BOATS_API_KEY');
Boats1.RequireApiKey := True;
Boats1.BaseURL := 'https://boatsea.imetroll.com';
Boats1.UseMapAreaSelection := True;
Boats1.AutoRequestOnAreaSelect := True;
Boats1.ShowAreaOnMap := True;
Boats1.IntervalSec := 10;
TileMap1.Boats := Boats1;
TileMap1.BoatsInfo := True;
Boats1.InfoWindow := MapInfoWindow1;
Boats1.Active := True;
// A) rysuj na mapie (pan zablokowany) → GET /v1/states?lamin=… lub lat/lon/radius_km
Boats1.BeginAreaSelect; // prostokąt
Boats1.BeginAreaSelect(bakRadius); // koło
// B) współrzędne
Boats1.ApplyCoords(54.52, 18.65, 30); // Zatoka Gdańska, 30 km
Boats1.ApplyCoords(60.15, 24.95, 20); // Helsinki – dobre pokrycie Digitraffic
if not Boats1.ApplyCoordsText(Edit1.Text) then
ShowMessage('oczekiwano lat, lon[, km]');
// C) prostokąt / nazwany obszar z katalogu GET /v1/areas
Boats1.ApplyBounds(53.5, 14, 55.5, 20.5); // lamin…lomax
Boats1.ApplyAreaId('gdansk'); // area=gdansk
Boats1.ApplyAreaId('finland-gulf');
Parametry Area + Display
// Parametry Area → query boatsea (zawsze wymagany obszar)
Boats1.Area.Kind := bakRectangle; // → lamin lomin lamax lomax
Boats1.Area.South := 53.5; Boats1.Area.West := 14;
Boats1.Area.North := 55.5; Boats1.Area.East := 20.5;
Boats1.Area.Kind := bakRadius; // → lat lon radius_km
Boats1.Area.CenterLat := 54.52;
Boats1.Area.CenterLon := 18.65;
Boats1.Area.RadiusKm := 30;
Boats1.Area.AreaId := 'gdansk'; // → area=gdansk (nadpisuje Kind przy ApplyAreaId)
// Display
Boats1.Display.Visible := True;
Boats1.Display.IconSize := 28;
Boats1.Display.ShowLabels := True;
Boats1.Display.AnimateMoves := True;
Boats1.RouteWindowSize := brwsLarge;
Boats1.RouteColor := '#00695C';
Boats1.Provider := bpBoatseaProxy; // lub bpSample
OnDataReady – JSON ships[]
// OnDataReady — surowy JSON boatsea (ships[])
procedure TForm1.Boats1DataReady(Sender: TObject; const JSON: string);
var
Root, Ships: TJSONValue;
Arr: TJSONArray;
I: Integer;
O: TJSONObject;
begin
MemoRaw.Text := JSON;
Root := TJSONObject.ParseJSONValue(JSON);
try
Caption := Format('źródło %s · %d statków',
[(Root as TJSONObject).GetValue('source').Value,
(Root as TJSONObject).GetValue('count').AsInteger]);
Arr := (Root as TJSONObject).GetValue('ships') as TJSONArray;
for I := 0 to Arr.Count - 1 do
begin
O := Arr.Items[I] as TJSONObject;
Memo1.Lines.Add(Format('%s %s sog=%.1f kn %s',
[O.GetValue('mmsi').Value,
O.GetValue('name').Value,
O.GetValue('sog').AsDouble,
O.GetValue('destination').Value]));
end;
finally
Root.Free;
end;
end;
Klik i trasa
procedure TForm1.BoatsClick(Sender: TObject; const State: TBoatsState);
begin
Memo1.Lines.Add(State.Name + ' ' + State.Mmsi);
end;
procedure TForm1.BtnShowRouteClick(Sender: TObject);
begin
Boats1.ShowRouteAsync(LastMmsi); // trasa + wykres prędkości (SOG) na mapie
end;Active: zaznaczenie obszaru lub współrzędne → automatyczne zapytanie
Boats1.Active = True na mapie: wybór obszaru (przycisk nawigacji Zaznacz obszar, BeginAreaSelect; mysz lub palec, pan/drag mapy zablokowany)
albo współrzędne (ApplyCoords / ApplyCoordsText / ApplyBounds / ApplyAreaId)
od razu wysyła zapytanie (RefreshAsync) do boatsea.imetroll.com/v1/states. Zapytanie trwające dla starego obszaru jest porzucane / anulowane.
Active = False → zmienia się tylko Area; zapytanie wywołujesz sam (Request).
| Źródło obszaru | TBoats.Area | Zapytanie (Active=True) |
|---|---|---|
| Prostokąt narysowany na mapie | bakRectangle + South/West/North/East | lamin lomin lamax lomax |
| Koło narysowane na mapie | bakRadius + CenterLat/Lon + RadiusKm | lat lon radius_km |
ApplyCoords(Lat, Lon, RadiusKm) | bakRadius + CenterLat/Lon + RadiusKm | lat lon radius_km |
ApplyCoords(Lat, Lon) (bez promienia) | bakRadius + CenterLat/Lon + RadiusKm, RadiusKm = Area.RadiusKm (domyślnie 50 km) | lat lon radius_km |
ApplyCoordsText(Edit1.Text) | "52.23, 21.01, 50" · "52,23; 21,01; 50" · "52.23 21.01 50km" · "lat=52.2 lon=21 r=30" (promień opcjonalny); False = nie rozpoznano (+ OnError) | |
ApplyBounds(S, W, N, E) | bakRectangle | lamin lomin lamax lomax |
ApplyAreaId(id) | Area.AreaId | area=gdansk |
| Właściwość / zdarzenie | Opis |
|---|---|
UseMapAreaSelection (True) | Zaznaczenie na mapie podmienia Area. |
AutoRequestOnAreaSelect (True) | Przy Active=True: nowy obszar → RefreshAsync. False = tylko Area. |
ShowAreaOnMap (True) | ApplyCoords / ApplyBounds rysują obszar na mapie (bez ponownego areaSelected). |
OnAreaApplied | Po podmianie Area (przed zapytaniem). |
Map.OnAreaSelected pozostaje Twoim zdarzeniem: TAircraft, TBoats i TWeather słuchają tej samej mapy równolegle
(TMapCore.AddAreaSelectedListener, multicast) – jedno zaznaczenie może zasilić wszystkie trzy.
Boats1.Active := True;
Boats1.BeginAreaSelect; // rysuj na mapie → zapytanie
Boats1.ApplyCoords(54.52, 18.65, 30); // współrzędne + promień [km] → zapytanie
if not Boats1.ApplyCoordsText(Edit1.Text) then ShowMessage('lat, lon[, km]');
boatsea.imetroll.com – GET (area-scoped jak fly)
GET https://boatsea.imetroll.com/v1/states?…&key= – statki tylko w żądanym obszarze (Digitraffic AIS; brak pokrycia → sample przefiltrowany, pole note). Brak obszaru → HTTP 400; nigdy cały świat. Auth: ?key= (jak weather/fly). Publiczne: /v1/health, /v1/areas.
| Tryb | GET params | TBoats |
|---|---|---|
| rectangle | lamin lomin lamax lomax | Area.Kind=bakRectangle + South/West/North/East · ApplyBounds / prostokąt z mapy |
| radius | lat lon radius_km (alias clat/clon/radius) | Area.Kind=bakRadius + CenterLat/Lon + RadiusKm · ApplyCoords / koło z mapy |
| named | area=gdansk | ApplyAreaId('gdansk') / Area.AreaId · katalog GET /v1/areas |
| Parametr | Typ | Opis |
|---|---|---|
lamin…lomax | float | Prostokąt WGS84; max ~40°×60° |
lat/lon/radius_km | float | Środek + promień [km] 0…1500; filtr haversine (koło) |
area | string | Id z katalogu: baltic, baltic-s, pl, pl-coast, gdansk, gdynia, szczecin, swinoujscie, kiel, oresund, finland-gulf, bothnia |
key | string | Klucz API (nginx) – wymagany na /v1/states, /v1/tracks, /v1/sample/states |
mmsi (+ tracks) | string | GET /v1/tracks?mmsi=&time=0 → path[{t,lat,lon,sog,cog,heading}] |
| JSON (states) | Opis |
|---|---|
time, source, count, note? | Unix time; digitraffic|sample; liczba; opcjonalna uwaga fallback |
bbox.mode + lamin… / lat,lon,radius_km / area | Echo obszaru zapytania |
ships[].mmsi, name, callsign, country, destination | Identyfikacja |
ships[].lat, lon, sog, cog, heading, draught, nav_status, ship_type(_name) | Pozycja + ruch [kn, °] |
ships[].ship_class, ship_class_label(_en), ship_class_color, icon_url, hazard, ais_type_desc, class_basis, length_m, beam_m | Klasa statku z AIS (tabela) + ikona typu |
GET /v1/states?lamin=53.5&lomin=14&lamax=55.5&lomax=20.5&key=YOUR_KEY GET /v1/states?lat=54.52&lon=18.65&radius_km=30&key=YOUR_KEY GET /v1/states?area=gdansk&key=YOUR_KEY GET /v1/tracks?mmsi=230661000&time=0&key=YOUR_KEY GET /v1/areas // publiczny katalog
Pełna dokumentacja serwera: boatsea.imetroll.com · demo: ▶ TBoats Active + area=
Klasy statków z AIS: kontenerowiec, tankowiec, chemikaliowiec, rybacki, okręt…
Każdy statek dostaje klasę (TBoatsState.ShipClass, JSON ship_class) – ikona na mapie (obrót po heading),
kolorowa etykieta, odznaka w oknie info i legenda w demo. Typ AIS (ITU-R M.1371) jest zgrubny (70 = cargo, 80 = tanker),
więc kontenerowiec / masowiec / chemikaliowiec / gazowiec wynikają z cyfry ładunku niebezpiecznego (x1–x4 = kat. A–D) i nazwy statku;
okręty i straż – z typu 35/55 albo prefiksu nazwy. Te same reguły ma serwer (ship_classes.py) i Delphi
(BoatsClassifyShip – fallback, gdy serwer nie poda ship_class). Pole class_basis: ais | ais+name | ais+hazard | name | mmsi.
| Ikona | ship_class | TBoatsShipClass | Etykieta | Źródło (AIS / MMSI / nazwa) |
|---|---|---|---|---|
container | bscContainer | Kontenerowiec | AIS 70–79 + nazwa armatora (MSC, MAERSK, CMA CGM, EVER…, CONTAINER/FEEDER) | |
bulk | bscBulk | Masowiec | AIS 70–79 + nazwa (BULK, ORE, COAL, GRAIN) | |
cargo | bscCargo | Statek towarowy | AIS 70–79 | |
tanker | bscTanker | Tankowiec | AIS 80–89 | |
chemical | bscChemical | Chemikaliowiec | AIS 81–84 (ładunek niebezpieczny kat. A–D) lub nazwa CHEM/STOLT/ODFJELL… | |
gas | bscGas | Gazowiec (LNG/LPG) | AIS 80–89 + nazwa LNG/LPG/GAS | |
passenger | bscPassenger | Pasażerski / prom | AIS 60–69 | |
hsc | bscHsc | Szybki (HSC) | AIS 40–49 | |
fishing | bscFishing | Rybacki | AIS 30 | |
military | bscMilitary | Okręt wojenny | AIS 35 lub prefiks nazwy ORP/HMS/HNLMS/FGS/USS… | |
law | bscLaw | Straż / policja | AIS 55 lub nazwa KBV/SG/POLICE/COAST GUARD | |
sar | bscSar | Ratowniczy (SAR) | AIS 51 | |
tug | bscTug | Holownik | AIS 31, 32, 52 | |
pilot | bscPilot | Pilotówka | AIS 50 | |
dredger | bscDredger | Pogłębiarka | AIS 33 | |
special | bscSpecial | Jednostka specjalna | AIS 34, 53, 54, 56–59 | |
sailing | bscSailing | Żaglowiec | AIS 36 | |
pleasure | bscPleasure | Rekreacyjny / jacht | AIS 37 | |
wig | bscWig | Ekranoplan (WIG) | AIS 20–29 | |
aton | bscAton | Znak nawigacyjny (AtoN) | MMSI 99xxxxxxx | |
beacon | bscBeacon | SART / MOB / EPIRB | MMSI 970/972/974 | |
other | bscOther | Inny | AIS 90–99 / inne | |
unknown | bscUnknown | Nieznany | brak typu AIS |
| Delphi | Opis |
|---|---|
Display.TypeIcons (True) | Ikona wg klasy z TypeIconBaseURL (domyślnie https://boatsea.imetroll.com/static/ships/ + <klasa>.svg); False / IconDataURL = jedna ikona. |
Display.ClassFilter | Zbiór klas do pokazania, np. [bscTanker, bscChemical, bscGas]; [] = wszystkie. |
Display.LabelField := blfShipClass | Etykieta = nazwa klasy zamiast nazwy statku. |
S.ShipClass, S.Hazard, S.LengthM, S.BeamM | Klasa, kat. ładunku A–D, wymiary z AIS (A+B / C+D). |
BoatsShipClassLabel(C[, English]), BoatsShipClassColor(C), BoatsShipClassId(C), BoatsShipClassIconURL(C) | Opis / kolor / id / URL ikony. |
// tylko tankowce, chemikaliowce i gazowce, etykieta = klasa
Boats1.Display.ClassFilter := [bscTanker, bscChemical, bscGas];
Boats1.Display.LabelField := blfShipClass;
procedure TForm1.Boats1BoatsClick(Sender: TObject; const S: TBoatsState);
begin
Caption := S.Name + ' – ' + BoatsShipClassLabel(S.ShipClass) +
IfThen(S.Hazard <> '', ' (ładunek niebezp. kat. ' + S.Hazard + ')', '');
end;
JSON: ships[].ship_class, ship_class_label, ship_class_label_en, ship_class_color, icon_url, hazard, ais_type_desc, class_basis, length_m, beam_m;
katalog GET /v1/ship-classes?key=; ikony publiczne /static/ships/<klasa>.svg.
Pełna lista API
Generowana z deklaracji w kodzie (Source/*.pas): wszystkie opublikowane właściwości i zdarzenia TBoats (także dziedziczone), właściwości publiczne i metody publiczne.
Właściwości opublikowane (19)
| Właściwość | Typ | Domyślnie | Opis |
|---|---|---|---|
Active | Boolean | False | |
BaseURL | string | Adres serwera Nominatim | |
ApiKey | string | Klucz API – tylko w kodzie, używany przez {apikey} | |
Key | string | ||
ApiKeyParam | string | ||
RequireApiKey | Boolean | True | |
Provider | TBoatsProvider | bpBoatseaProxy | |
Area | TBoatsArea | Zaznaczony obszar (kształt, punkty, prostokąt, koło) | |
Display | TBoatsDisplay | ||
IntervalSec | Integer | BOATS_DEFAULT_INTERVAL_SEC | |
TimeoutMs | Integer | BOATS_DEFAULT_TIMEOUT_MS | |
Map | TComponent | ||
InfoWindow | TMapInfoWindow | ||
AutoLinkComponents | Boolean | True | Automatycznie łącz pierwszy dostawcę wyszukiwania / TMapAreas / TEditMap z formy |
RouteWindowSize | TBoatsRouteWindowSize | brwsLarge | |
RouteColor | string | ||
UseMapAreaSelection | Boolean | True | |
AutoRequestOnAreaSelect | Boolean | True | |
ShowAreaOnMap | Boolean | True |
Zdarzenia (5)
| Zdarzenie | Typ | Opis |
|---|---|---|
OnAreaApplied | TNotifyEvent | |
OnDataReady | TBoatsDataReadyEvent | |
OnError | TBoatsErrorEvent | Każdy błąd (ErrorKind, HTTP, komunikat PL/EN) |
OnBoatsClick | TBoatsClickEvent | |
OnRouteReady | TBoatsRouteReadyEvent |
Właściwości publiczne – tylko w kodzie (6)
| Właściwość | Typ | Opis |
|---|---|---|
Data | TBoatsSnapshot | |
LastTrack | TBoatsTrack | |
Busy | Boolean | Trwa zapytanie |
LastErrorKind | TBoatsErrorKind | |
LastError | string | |
LastRefresh | TDateTime |
Metody publiczne (19)
| Metoda | Opis |
|---|---|
function Refresh: Boolean | Ponownie wczytuje hierarchię i pliki |
function Request: Boolean | |
procedure RefreshAsync | |
procedure Cancel | |
function DataJSON: string | |
procedure DetectLinkedComponents | Wyszukuje na formie i łączy dostawcę / TMapAreas / TEditMap |
function MapCore: TMapCore | IMapCoreProvider – rdzeń dla TEditMapRaster |
procedure UpdateDisplay | |
procedure ClearDisplay | |
function ShowRoute(const Mmsi: string): Boolean | |
procedure ShowRouteAsync(const Mmsi: string) | |
procedure ClearRoute | |
function FindBoat(const Mmsi: string; out S: TBoatsState): Boolean | |
procedure BeginAreaSelect | |
procedure CancelAreaSelect | |
procedure ApplyCoords(const Lat, Lon: Double; const RadiusKm: Double = 0) | |
function ApplyCoordsText(const Text: string): Boolean | |
procedure ApplyBounds(const South, West, North, East: Double) | |
procedure ApplyAreaId(const AreaId: string) |
Pełny opis wszystkich właściwości: dokumentacja komponentów map.
Zobacz też
TAircraft
Middleware ADS-B: forma/mapa ↔ TAircraft ↔ fly.imetroll.com (GET + key=) → samoloty na mapie, InfoWindow, trasa + wykres wysokości.
TTileMapVCL
Mapa kafelkowa dla aplikacji Windows (VCL) – OpenStreetMap, zdjęcia satelitarne, mapa terenu.
T3DMapVCL
Mapa wektorowa 2D/3D z budynkami, motywem dziennym/nocnym i własnymi kaflami z mapa.imetroll.com.