diff --git a/.travis.yml b/.travis.yml
index 90e0767..eb7b70e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,9 +3,9 @@ language: python
python:
- "3.6"
-branches:
- only:
- - master
+#branches:
+# only:
+# - master
script:
- pip install .
diff --git a/README.md b/README.md
index e44b6ce..1826dbc 100644
--- a/README.md
+++ b/README.md
@@ -112,6 +112,60 @@ for vessel in vessels.models:
vessel.max_speed
```
+
[PS03] Vessel Positions of a Dynamic Fleet
+
+```python
+vessels = api.dynamic_fleet_vessel_positions(time_span=10)
+
+for vessel in vessels.models:
+ vessel.mmsi
+ vessel.imo
+ vessel.ship_id
+ vessel.longitude
+ vessel.latitude
+ vessel.speed
+ vessel.heading
+ vessel.status
+ vessel.course
+ vessel.timestamp
+ vessel.dsrc
+ vessel.utc_seconds
+ vessel.ship_name
+ vessel.ship_type
+ vessel.call_sign
+ vessel.flag
+ vessel.length
+ vessel.width
+ vessel.grt
+ vessel.dwt
+ vessel.draught
+ vessel.year_built
+ vessel.rot
+ vessel.type_name
+ vessel.ais_type_summary
+ vessel.destination
+ vessel.eta
+ vessel.current_port
+ vessel.last_port
+ vessel.last_port_time
+ vessel.current_port_id
+ vessel.current_port_unlocode
+ vessel.current_port_country
+ vessel.last_port_id
+ vessel.last_port_unlocode
+ vessel.last_port_country
+ vessel.next_port_id
+ vessel.next_port_unlocode
+ vessel.next_port_name
+ vessel.next_port_country
+ vessel.eta_calc
+ vessel.eta_updated
+ vessel.distance_to_go
+ vessel.distance_travelled
+ vessel.awg_speed
+ vessel.max_speed
+```
+
Voyage Info
[VI03] Port Distance and Routes
diff --git a/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/__init__.py b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/models.py b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/models.py
new file mode 100644
index 0000000..2250252
--- /dev/null
+++ b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/models.py
@@ -0,0 +1,186 @@
+from marinetrafficapi.models import Model
+from marinetrafficapi.fields import NumberField, RealNumberField, DatetimeField, TextField
+
+
+class DynamicFleetVesselPosition(Model):
+ """Get positional information for a set of predefined vessels."""
+
+ mmsi = NumberField(index='MMSI',
+ desc="Maritime Mobile Service Identity - a nine-digit number "
+ "sent in digital form over a radio frequency that identifies "
+ "the vessel's transmitter station")
+
+ imo = NumberField(index='IMO',
+ desc="International Maritime Organisation number - a "
+ "seven-digit number that uniquely identifies vessels")
+
+ ship_id = NumberField(index='SHIP_ID',
+ desc="A uniquely assigned ID by MarineTraffic "
+ "for the subject vessel")
+
+ longitude = RealNumberField(index='LON',
+ desc="A geographic coordinate that specifies the "
+ "east-west position of the vessel on the "
+ "Earth's surface")
+
+ latitude = RealNumberField(index='LAT',
+ desc="a geographic coordinate that specifies the "
+ "north-south position of the vessel on the "
+ "Earth's surface")
+
+ speed = NumberField(index='SPEED',
+ desc="The speed (in knots x10) that the subject vessel is "
+ "reporting according to AIS transmissions")
+
+ heading = NumberField(index='HEADING',
+ desc="The heading (in degrees) that the subject vessel is "
+ "reporting according to AIS transmissions")
+
+ status = NumberField(index='STATUS',
+ desc="The AIS Navigational Status of the subject vessel as "
+ "input by the vessel's crew - more. There might be "
+ "discrepancies with the vessel's detail page when vessel "
+ "speed is near zero (0) knots.")
+
+ course = NumberField(index='COURSE',
+ desc="The course (in degrees) that the subject vessel is "
+ "reporting according to AIS transmissions")
+
+ timestamp = DatetimeField(index='TIMESTAMP',
+ desc="The date and time (in UTC) that the subject vessel's "
+ "position was recorded by MarineTraffic",
+ format='%Y-%m-%dT%H:%M:%S')
+
+ dsrc = TextField(index='DSRC',
+ desc="Data Source - Defines whether the transmitted AIS data was "
+ "received by a Terrestrial or a Satellite AIS Station")
+
+ utc_seconds = NumberField(index='UTC_SECONDS',
+ desc="The time slot that the subject vessel uses "
+ "to transmit information")
+
+ ship_name = TextField(index='SHIPNAME',
+ desc="The Shipname of the subject vessel")
+
+ ship_type = NumberField(index='SHIPTYPE',
+ desc="The Shiptype of the subject vessel "
+ "according to AIS transmissions")
+
+ call_sign = TextField(index='CALLSIGN',
+ desc="A uniquely designated identifier for "
+ "the vessel's transmitter station")
+
+ flag = TextField(index='FLAG',
+ desc="The flag of the subject vessel according to AIS transmissions")
+
+ length = RealNumberField(index='LENGTH',
+ desc="The overall Length (in metres) of the subject vessel")
+
+ width = RealNumberField(index='WIDTH',
+ desc="The Breadth (in metres) of the subject vessel")
+
+ grt = NumberField(index='GRT',
+ desc="Gross Tonnage - unitless measure that calculates the "
+ "moulded volume of all enclosed spaces of a ship")
+
+ dwt = NumberField(index='DWT',
+ desc="Deadweight - a measure (in metric tons) of how much weight a "
+ "vessel can safely carry (excluding the vessel's own weight")
+
+ draught = NumberField(index='DRAUGHT',
+ desc="The Draught (in metres x10) of the subject vessel "
+ "according to the AIS transmissions")
+
+ year_built = NumberField(index='YEAR_BUILT',
+ desc="The year that the subject vessel was built")
+
+ rot = NumberField(index='ROT',
+ desc="Rate of Turn")
+
+ type_name = TextField(index='TYPE_NAME',
+ desc="The Type of the subject vessel")
+
+ ais_type_summary = TextField(index='AIS_TYPE_SUMMARY',
+ desc="Further explanation of the SHIPTYPE ID")
+
+ destination = TextField(index='DESTINATION',
+ desc="The Destination of the subject vessel "
+ "according to the AIS transmissions")
+
+ eta = DatetimeField(index='ETA',
+ desc="The Estimated Time of Arrival to Destination of the "
+ "subject vessel according to the AIS transmissions",
+ format='%Y-%m-%dT%H:%M:%S')
+
+ current_port = TextField(index='CURRENT_PORT',
+ desc="The name of the Port the subject vessel is "
+ "currently in (NULL if the vessel is underway)")
+
+ last_port = TextField(index='LAST_PORT',
+ desc="The Name of the Last Port the vessel has visited")
+
+ last_port_time = DatetimeField(index='LAST_PORT_TIME',
+ desc="The Date and Time (in UTC) that the "
+ "subject vessel departed from the Last Port",
+ format='%Y-%m-%dT%H:%M:%S')
+
+ current_port_id = NumberField(index='CURRENT_PORT_ID',
+ desc="A uniquely assigned ID by "
+ "MarineTraffic for the Current Port")
+
+ current_port_unlocode = TextField(index='CURRENT_PORT_UNLOCODE',
+ desc="A uniquely assigned ID by "
+ "United Nations for the Current Port")
+
+ current_port_country = TextField(index='CURRENT_PORT_COUNTRY',
+ desc="The Country that the Current Port is located at")
+
+ last_port_id = NumberField(index='LAST_PORT_ID',
+ desc="A uniquely assigned ID by MarineTraffic for the Last Port")
+
+ last_port_unlocode = TextField(index='LAST_PORT_UNLOCODE',
+ desc="A uniquely assigned ID by "
+ "United Nations for the Last Port")
+
+ last_port_country = TextField(index='LAST_PORT_COUNTRY',
+ desc="The Country that the Last Port is located at")
+
+ next_port_id = NumberField(index='NEXT_PORT_ID',
+ desc="A uniquely assigned ID by MarineTraffic for the Next Port")
+
+ next_port_unlocode = TextField(index='NEXT_PORT_UNLOCODE',
+ desc="A uniquely assigned ID by "
+ "United Nations for the Next Port")
+
+ next_port_name = TextField(index='NEXT_PORT_NAME',
+ desc="The Name of the Next Port as derived by MarineTraffic "
+ "based on the subject vessel's reported Destination")
+
+ next_port_country = TextField(index='NEXT_PORT_COUNTRY',
+ desc="The Country that the Next Port is located at")
+
+ eta_calc = DatetimeField(index='ETA_CALC',
+ desc="The Estimated Time of Arrival to Destination of "
+ "the subject vessel according to the MarineTraffic calculations",
+ format='%Y-%m-%dT%H:%M:%S')
+
+ eta_updated = DatetimeField(index='ETA_UPDATED',
+ desc="The date and time (in UTC) that the "
+ "ETA was calculated by MarineTraffic",
+ format='%Y-%m-%dT%H:%M:%S')
+
+ distance_to_go = NumberField(index='DISTANCE_TO_GO',
+ desc="The Remaining Distance (in NM) for the subject "
+ "vessel to reach the reported Destination")
+
+ distance_travelled = NumberField(index='DISTANCE_TRAVELLED',
+ desc="The Distance (in NM) that the subject "
+ "vessel has travelled since departing from Last Port")
+
+ awg_speed = RealNumberField(index='AVG_SPEED',
+ desc="The average speed calculated for the subject "
+ "vessel during the latest voyage (port to port)")
+
+ max_speed = RealNumberField(index='MAX_SPEED',
+ desc="The maximum speed reported by the subject "
+ "vessel during the latest voyage (port to port)")
diff --git a/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/query_params.py b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/query_params.py
new file mode 100644
index 0000000..9569531
--- /dev/null
+++ b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/query_params.py
@@ -0,0 +1,16 @@
+from marinetrafficapi.query_params import QueryParams
+
+
+class PS03QueryParams(QueryParams):
+ """Query params params for PS02 API call."""
+
+ params = {
+ # The maximum age, in minutes, of the returned positions.
+ # Maximum value for terrestrial coverage is 60.
+ # Maximum value for satellite coverage is 180.
+ 'time_span': 'timespan',
+
+ # Data filter: Vessel type.
+ # (2=Fishing / 4=High Speed Craft / 6=Passenger / 7=Cargo / 8=Tanker)
+ 'ship_type': 'shiptype',
+ }
diff --git a/marinetrafficapi/vessels_positions/client.py b/marinetrafficapi/vessels_positions/client.py
index e67845f..95fb62d 100644
--- a/marinetrafficapi/vessels_positions/client.py
+++ b/marinetrafficapi/vessels_positions/client.py
@@ -10,6 +10,11 @@
from marinetrafficapi.vessels_positions.\
PS02_vessel_positions_of_a_static_fleet.query_params import PS02QueryParams
+from marinetrafficapi.vessels_positions.\
+ PS03_vessel_position_of_a_dynamic_fleet.query_params import PS03QueryParams
+from marinetrafficapi.vessels_positions.\
+ PS03_vessel_position_of_a_dynamic_fleet.models import DynamicFleetVesselPosition
+
class VesselPositions:
"""Retrieve forecasted information for any vessel.
@@ -38,3 +43,15 @@ class VesselPositions:
'protocol': 'jsono'
}
)
+
+ # PS03 - Monitor vessel activity for your MarineTraffic fleet(s)
+ dynamic_fleet_vessel_positions = bind_request(
+ api_path='/exportvessels',
+ model=DynamicFleetVesselPosition,
+ query_parameters=PS03QueryParams,
+ default_parameters={
+ 'v': '8',
+ 'msgtype': 'simple',
+ 'protocol': 'jsono'
+ }
+ )
diff --git a/setup.py b/setup.py
index ade9547..d89c09e 100644
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@ def readme():
setup(
name='Marine Traffic API',
- version='0.3.0',
+ version='0.4.0',
description='Marine Traffic Client Api',
long_description=readme(),
@@ -27,7 +27,7 @@ def readme():
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.7',
],
- keywords='marine traffic, api, cruise, distance, port',
+ keywords='marine traffic, api, cruise, distance, port, vessel, track, fleet',
packages=find_packages(),
install_requires=[
diff --git a/tests/responses/ps03_response.json b/tests/responses/ps03_response.json
new file mode 100644
index 0000000..b2e2c00
--- /dev/null
+++ b/tests/responses/ps03_response.json
@@ -0,0 +1,146 @@
+[
+ {
+ "MMSI":"304010417",
+ "IMO":"9015462",
+ "SHIP_ID":"359396",
+ "LAT":"47.758499",
+ "LON":"-5.154223",
+ "SPEED":"74",
+ "HEADING":"329",
+ "COURSE":"327",
+ "STATUS":"0",
+ "TIMESTAMP":"2017-05-19T09:39:57",
+ "DSRC":"TER",
+ "UTC_SECONDS":"54",
+ "SHIPNAME":"DORNUM",
+ "SHIPTYPE":"70",
+ "CALLSIGN":"V2OZ",
+ "FLAG":"AG",
+ "LENGTH":"81.7900009",
+ "WIDTH":"11.3000002",
+ "GRT":"1662",
+ "DWT":"2369",
+ "DRAUGHT":"44",
+ "YEAR_BUILT":"1993",
+ "ROT":"6",
+ "TYPE_NAME":"General Cargo",
+ "AIS_TYPE_SUMMARY":"Cargo",
+ "DESTINATION":"GREENORE",
+ "ETA":"2017-05-20T08:00:00",
+ "CURRENT_PORT":"",
+ "LAST_PORT":"BILBAO ANCH",
+ "LAST_PORT_TIME":"2017-05-16T15:37:00",
+ "CURRENT_PORT_ID":"",
+ "CURRENT_PORT_UNLOCODE":"",
+ "CURRENT_PORT_COUNTRY":"",
+ "LAST_PORT_ID":"20648",
+ "LAST_PORT_UNLOCODE":"",
+ "LAST_PORT_COUNTRY":"ES",
+ "NEXT_PORT_ID":"251",
+ "NEXT_PORT_UNLOCODE":"IEGRN",
+ "NEXT_PORT_NAME":"GREENORE",
+ "NEXT_PORT_COUNTRY":"IE",
+ "ETA_CALC":"2017-05-21T09:55:00",
+ "ETA_UPDATED":"2017-05-19T09:07:00",
+ "DISTANCE_TO_GO":"407",
+ "DISTANCE_TRAVELLED":"364",
+ "AVG_SPEED":"8.5",
+ "MAX_SPEED":"8.80000019"
+ },
+ {
+ "MMSI":"215819000",
+ "IMO":"9034731",
+ "SHIP_ID":"150559",
+ "LAT":"47.926899",
+ "LON":"-5.531450",
+ "SPEED":"122",
+ "HEADING":"162",
+ "COURSE":"157",
+ "STATUS":"0",
+ "TIMESTAMP":"2017-05-19T09:44:27",
+ "DSRC":"TER",
+ "UTC_SECONDS":"28",
+ "SHIPNAME":"TOUR MARGAUX",
+ "SHIPTYPE":"81",
+ "CALLSIGN":"9HBW8",
+ "FLAG":"MT",
+ "LENGTH":"113.639999",
+ "WIDTH":"17.7000008",
+ "GRT":"5499",
+ "DWT":"8674",
+ "DRAUGHT":"64",
+ "YEAR_BUILT":"1993",
+ "ROT":"0",
+ "TYPE_NAME":"Oil/Chemical Tanker",
+ "AIS_TYPE_SUMMARY":"Tanker",
+ "DESTINATION":"BILBAO",
+ "ETA":"2017-05-20T16:00:00",
+ "CURRENT_PORT":"",
+ "LAST_PORT":"HAMBURG",
+ "LAST_PORT_TIME":"2017-05-16T15:04:00",
+ "CURRENT_PORT_ID":"",
+ "CURRENT_PORT_UNLOCODE":"",
+ "CURRENT_PORT_COUNTRY":"",
+ "LAST_PORT_ID":"172",
+ "LAST_PORT_UNLOCODE":"DEHAM",
+ "LAST_PORT_COUNTRY":"DE",
+ "NEXT_PORT_ID":"1271",
+ "NEXT_PORT_UNLOCODE":"ESBIO",
+ "NEXT_PORT_NAME":"BILBAO",
+ "NEXT_PORT_COUNTRY":"ES",
+ "ETA_CALC":"2017-05-20T11:27:00",
+ "ETA_UPDATED":"2017-05-19T09:13:00",
+ "DISTANCE_TO_GO":"295",
+ "DISTANCE_TRAVELLED":"782",
+ "AVG_SPEED":"12",
+ "MAX_SPEED":"13.3999996"
+ },
+ {
+ "MMSI":"255925000",
+ "IMO":"9184433",
+ "SHIP_ID":"300518",
+ "LAT":"47.942631",
+ "LON":"-5.116510",
+ "SPEED":"79",
+ "HEADING":"316",
+ "COURSE":"311",
+ "STATUS":"0",
+ "TIMESTAMP":"2017-05-19T09:43:53",
+ "DSRC":"TER",
+ "UTC_SECONDS":"52",
+ "SHIPNAME":"BULNES",
+ "SHIPTYPE":"70",
+ "CALLSIGN":"CQWC",
+ "FLAG":"PT",
+ "LENGTH":"85.6600037",
+ "WIDTH":"13.0200005",
+ "GRT":"2469",
+ "DWT":"3700",
+ "DRAUGHT":"58",
+ "YEAR_BUILT":"2001",
+ "ROT":"22",
+ "TYPE_NAME":"General Cargo",
+ "AIS_TYPE_SUMMARY":"Cargo",
+ "DESTINATION":"SAINT MALO",
+ "ETA":"2017-05-20T09:00:00",
+ "CURRENT_PORT":"",
+ "LAST_PORT":"BAYONNE",
+ "LAST_PORT_TIME":"2017-05-17T16:05:00",
+ "CURRENT_PORT_ID":"",
+ "CURRENT_PORT_UNLOCODE":"",
+ "CURRENT_PORT_COUNTRY":"",
+ "LAST_PORT_ID":"508",
+ "LAST_PORT_UNLOCODE":"FRBAY",
+ "LAST_PORT_COUNTRY":"FR",
+ "NEXT_PORT_ID":"500",
+ "NEXT_PORT_UNLOCODE":"FRSML",
+ "NEXT_PORT_NAME":"SAINT MALO",
+ "NEXT_PORT_COUNTRY":"FR",
+ "ETA_CALC":"2017-05-20T09:11:00",
+ "ETA_UPDATED":"2017-05-19T09:43:00",
+ "DISTANCE_TO_GO":"183",
+ "DISTANCE_TRAVELLED":"333",
+ "AVG_SPEED":"7.80000019",
+ "MAX_SPEED":"8.5"
+ }
+]
\ No newline at end of file
diff --git a/tests/test_ps03.py b/tests/test_ps03.py
new file mode 100644
index 0000000..b60e5a9
--- /dev/null
+++ b/tests/test_ps03.py
@@ -0,0 +1,67 @@
+import os
+import unittest
+from datetime import datetime
+
+from marinetrafficapi.client import Client
+
+
+class PS03Response(unittest.TestCase):
+
+ def setUp(self):
+ self._api_key = '_api_key_'
+ self.fake_ok_response_path_json = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'tests', 'responses', 'ps03_response.json'
+ )
+
+ def test_ok_response_json(self):
+ request = Client(self._api_key,
+ fake_response_path=self.fake_ok_response_path_json)\
+ .dynamic_fleet_vessel_positions()
+
+ self.assertEqual(request.models[1].mmsi, 215819000)
+ self.assertEqual(request.models[1].imo, 9034731)
+ self.assertEqual(request.models[1].ship_id, 150559)
+ self.assertEqual(request.models[1].latitude, 47.926899)
+ self.assertEqual(request.models[1].longitude, -5.531450)
+ self.assertEqual(request.models[1].speed, 122)
+ self.assertEqual(request.models[1].heading, 162)
+ self.assertEqual(request.models[1].course, 157)
+ self.assertEqual(request.models[1].status, 0)
+ self.assertEqual(request.models[1].timestamp, datetime(2017, 5, 19, 9, 44, 27))
+ self.assertEqual(request.models[1].dsrc, 'TER')
+ self.assertEqual(request.models[1].utc_seconds, 28)
+ self.assertEqual(request.models[1].ship_name, 'TOUR MARGAUX')
+ self.assertEqual(request.models[1].ship_type, 81)
+ self.assertEqual(request.models[1].call_sign, '9HBW8')
+ self.assertEqual(request.models[1].flag, 'MT')
+ self.assertEqual(request.models[1].length, 113.639999)
+ self.assertEqual(request.models[1].width, 17.7000008)
+ self.assertEqual(request.models[1].grt, 5499)
+ self.assertEqual(request.models[1].dwt, 8674)
+ self.assertEqual(request.models[1].draught, 64)
+ self.assertEqual(request.models[1].year_built, 1993)
+ self.assertEqual(request.models[1].rot, 0)
+ self.assertEqual(request.models[1].type_name, 'Oil/Chemical Tanker')
+ self.assertEqual(request.models[1].ais_type_summary, 'Tanker')
+ self.assertEqual(request.models[1].destination, 'BILBAO')
+ self.assertEqual(request.models[1].current_port, '')
+ self.assertEqual(request.models[1].current_port_id, 0)
+ self.assertEqual(request.models[1].current_port_unlocode, '')
+ self.assertEqual(request.models[1].current_port_country, '')
+ self.assertEqual(request.models[1].last_port, 'HAMBURG')
+ self.assertEqual(request.models[1].last_port_time, datetime(2017, 5, 16, 15, 4, 0))
+ self.assertEqual(request.models[1].eta, datetime(2017, 5, 20, 16, 0, 0))
+ self.assertEqual(request.models[1].last_port_id, 172)
+ self.assertEqual(request.models[1].last_port_unlocode, 'DEHAM')
+ self.assertEqual(request.models[1].last_port_country, 'DE')
+ self.assertEqual(request.models[1].next_port_id, 1271)
+ self.assertEqual(request.models[1].next_port_unlocode, 'ESBIO')
+ self.assertEqual(request.models[1].next_port_name, 'BILBAO')
+ self.assertEqual(request.models[1].next_port_country, 'ES')
+ self.assertEqual(request.models[1].eta_calc, datetime(2017, 5, 20, 11, 27, 0))
+ self.assertEqual(request.models[1].eta_updated, datetime(2017, 5, 19, 9, 13, 0))
+ self.assertEqual(request.models[1].distance_to_go, 295)
+ self.assertEqual(request.models[1].distance_travelled, 782)
+ self.assertEqual(request.models[1].awg_speed, 12)
+ self.assertEqual(request.models[1].max_speed, 13.3999996)