Komponenty Delphi pomoc
Start › Komponenty Delphi › TBoats

TBoats

Middleware AIS: forma/mapa ↔ TBoats ↔ boatsea.imetroll.com (GET + key=) → statki na mapie, InfoWindow, trasa + wykres SOG.

VCLFMXniewidoczny
▶ Zobacz na żywo – demo HTML

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

TBoats – podgląd poglądowy
Statki na Bałtyku (boatsea.imetroll.com): ikona + nazwa, klik → InfoWindow, Pokaż trasę → polyline + wykres SOG. Podgląd poglądowy (nie zrzut z Delphi) – prawdziwa strona mapy komponentu w ramce imitującej formę.

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

Serwer: https://boatsea.imetroll.com (nginx + ships_proxy → Digitraffic AIS, fallback sample Bałtyk/PL).
Ten sam wzorzec co TAircraft / fly.imetroll.com.
Moduł RTL; paleta Maps; MapComponentsRT.

Właściwości

WłaściwośćOpis
ApiKey / Key, RequireApiKey, ApiKeyParamJak TWeather/TAircraft – key= na query.
BaseURLDomyślnie https://boatsea.imetroll.com
Area.Kind / South|West|North|EastbakRectangle → lamin/lomin/lamax/lomax (domyślnie Bałtyk PL).
Area.CenterLat/Lon + RadiusKmbakRadius → lat/lon/radius_km (domyślnie Zatoka Gdańska 50 km).
Area.AreaIdNazwany obszar boatsea (baltic, gdansk, szczecin, finland-gulf…) → area=.
UseMapAreaSelection, AutoRequestOnAreaSelect, ShowAreaOnMapZaznaczenie mapy / współrzędne → Area; Active=True → RefreshAsync.
IntervalSec, ActiveTimer; Active=True = auto-zapytanie po zmianie obszaru.
Display.*Visible, IconDataURL, IconSize, ShowLabels, LabelField (blfShipClass), Opacity, AnimateMoves, TypeIcons, TypeIconBaseURL, ClassFilter.
Map, InfoWindow, AutoLinkComponentsPowiązania.
RouteWindowSize, RouteColorOkno trasy small/large + kolor linii.
ProviderbpBoatseaProxy / bpSample.

Zdarzenia

ZdarzenieOpis
OnDataReady(JSON)Po udanym states.
OnBoatsClick(State)Klik ikony statku.
OnRouteReady(Track, RouteJSON)Po pobraniu trasy.
OnAreaAppliedArea podmienione z mapy / współrzędnych.
OnErrorbekUnauthorized / bekEmpty (brak obszaru) / sieć / HTTP / parse.

Metody

MetodaOpis
Request / Refresh / RefreshAsyncPobranie stanów (zawsze dla Area).
BeginAreaSelect / CancelAreaSelectRysowanie prostokąta / koła na mapie (pan-lock).
ApplyMapArea / ApplyCoords / ApplyCoordsText / ApplyBounds / ApplyAreaIdObszar z mapy / współrzędnych; Active → RefreshAsync.
ShowRoute / ShowRouteAsync / ClearRouteTrasa + wykres SOG.
UpdateDisplay / ClearDisplayWypchnięcie setBoats.
DetectLinkedComponentsMap + 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 obszaruTBoats.AreaZapytanie (Active=True)
Prostokąt narysowany na mapiebakRectangle + South/West/North/Eastlamin lomin lamax lomax
Koło narysowane na mapiebakRadius + CenterLat/Lon + RadiusKmlat lon radius_km
ApplyCoords(Lat, Lon, RadiusKm)bakRadius + CenterLat/Lon + RadiusKmlat 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)bakRectanglelamin lomin lamax lomax
ApplyAreaId(id)Area.AreaIdarea=gdansk
Właściwość / zdarzenieOpis
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).
OnAreaAppliedPo 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.

TrybGET paramsTBoats
rectanglelamin lomin lamax lomaxArea.Kind=bakRectangle + South/West/North/East · ApplyBounds / prostokąt z mapy
radiuslat lon radius_km (alias clat/clon/radius)Area.Kind=bakRadius + CenterLat/Lon + RadiusKm · ApplyCoords / koło z mapy
namedarea=gdanskApplyAreaId('gdansk') / Area.AreaId · katalog GET /v1/areas
ParametrTypOpis
lamin…lomaxfloatProstokąt WGS84; max ~40°×60°
lat/lon/radius_kmfloatŚrodek + promień [km] 0…1500; filtr haversine (koło)
areastringId z katalogu: baltic, baltic-s, pl, pl-coast, gdansk, gdynia, szczecin, swinoujscie, kiel, oresund, finland-gulf, bothnia
keystringKlucz API (nginx) – wymagany na /v1/states, /v1/tracks, /v1/sample/states
mmsi (+ tracks)stringGET /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 / areaEcho obszaru zapytania
ships[].mmsi, name, callsign, country, destinationIdentyfikacja
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_mKlasa 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.

Ikonaship_classTBoatsShipClassEtykietaŹródło (AIS / MMSI / nazwa)
containerbscContainerKontenerowiecAIS 70–79 + nazwa armatora (MSC, MAERSK, CMA CGM, EVER…, CONTAINER/FEEDER)
bulkbscBulkMasowiecAIS 70–79 + nazwa (BULK, ORE, COAL, GRAIN)
cargobscCargoStatek towarowyAIS 70–79
tankerbscTankerTankowiecAIS 80–89
chemicalbscChemicalChemikaliowiecAIS 81–84 (ładunek niebezpieczny kat. A–D) lub nazwa CHEM/STOLT/ODFJELL…
gasbscGasGazowiec (LNG/LPG)AIS 80–89 + nazwa LNG/LPG/GAS
passengerbscPassengerPasażerski / promAIS 60–69
hscbscHscSzybki (HSC)AIS 40–49
fishingbscFishingRybackiAIS 30
militarybscMilitaryOkręt wojennyAIS 35 lub prefiks nazwy ORP/HMS/HNLMS/FGS/USS…
lawbscLawStraż / policjaAIS 55 lub nazwa KBV/SG/POLICE/COAST GUARD
sarbscSarRatowniczy (SAR)AIS 51
tugbscTugHolownikAIS 31, 32, 52
pilotbscPilotPilotówkaAIS 50
dredgerbscDredgerPogłębiarkaAIS 33
specialbscSpecialJednostka specjalnaAIS 34, 53, 54, 56–59
sailingbscSailingŻaglowiecAIS 36
pleasurebscPleasureRekreacyjny / jachtAIS 37
wigbscWigEkranoplan (WIG)AIS 20–29
atonbscAtonZnak nawigacyjny (AtoN)MMSI 99xxxxxxx
beaconbscBeaconSART / MOB / EPIRBMMSI 970/972/974
otherbscOtherInnyAIS 90–99 / inne
unknownbscUnknownNieznanybrak typu AIS
DelphiOpis
Display.TypeIcons (True)Ikona wg klasy z TypeIconBaseURL (domyślnie https://boatsea.imetroll.com/static/ships/ + <klasa>.svg); False / IconDataURL = jedna ikona.
Display.ClassFilterZbiór klas do pokazania, np. [bscTanker, bscChemical, bscGas]; [] = wszystkie.
Display.LabelField := blfShipClassEtykieta = nazwa klasy zamiast nazwy statku.
S.ShipClass, S.Hazard, S.LengthM, S.BeamMKlasa, 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śćTypDomyślnieOpis
ActiveBooleanFalse
BaseURLstringAdres serwera Nominatim
ApiKeystringKlucz API – tylko w kodzie, używany przez {apikey}
Keystring
ApiKeyParamstring
RequireApiKeyBooleanTrue
ProviderTBoatsProviderbpBoatseaProxy
AreaTBoatsAreaZaznaczony obszar (kształt, punkty, prostokąt, koło)
DisplayTBoatsDisplay
IntervalSecIntegerBOATS_DEFAULT_INTERVAL_SEC
TimeoutMsIntegerBOATS_DEFAULT_TIMEOUT_MS
MapTComponent
InfoWindowTMapInfoWindow
AutoLinkComponentsBooleanTrueAutomatycznie łącz pierwszy dostawcę wyszukiwania / TMapAreas / TEditMap z formy
RouteWindowSizeTBoatsRouteWindowSizebrwsLarge
RouteColorstring
UseMapAreaSelectionBooleanTrue
AutoRequestOnAreaSelectBooleanTrue
ShowAreaOnMapBooleanTrue

Zdarzenia (5)

ZdarzenieTypOpis
OnAreaAppliedTNotifyEvent
OnDataReadyTBoatsDataReadyEvent
OnErrorTBoatsErrorEventKażdy błąd (ErrorKind, HTTP, komunikat PL/EN)
OnBoatsClickTBoatsClickEvent
OnRouteReadyTBoatsRouteReadyEvent

Właściwości publiczne – tylko w kodzie (6)

WłaściwośćTypOpis
DataTBoatsSnapshot
LastTrackTBoatsTrack
BusyBooleanTrwa zapytanie
LastErrorKindTBoatsErrorKind
LastErrorstring
LastRefreshTDateTime

Metody publiczne (19)

MetodaOpis
function Refresh: BooleanPonownie wczytuje hierarchię i pliki
function Request: Boolean
procedure RefreshAsync
procedure Cancel
function DataJSON: string
procedure DetectLinkedComponentsWyszukuje na formie i łączy dostawcę / TMapAreas / TEditMap
function MapCore: TMapCoreIMapCoreProvider – 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ż