No edit summary |
No edit summary |
||
Line 29: | Line 29: | ||
** and finally adding the layer to shipping_lane_layers. Note that you can also set ship_types and then map this layer's types to those ship types using layer_type_ship_type_mapping (see below). | ** and finally adding the layer to shipping_lane_layers. Note that you can also set ship_types and then map this layer's types to those ship types using layer_type_ship_type_mapping (see below). | ||
=== | === Base Parameters === | ||
"shipping_lane_layers": [ "NS_Shipping_Routes" ], | "shipping_lane_layers": [ "NS_Shipping_Routes" ], | ||
"country_border_layer": "NS_EEZ", | |||
"shipping_lane_point_merge_distance": 10000, | |||
"shipping_lane_subdivide_distance": 50000, | |||
"shipping_lane_implicit_distance_limit": 200000, | |||
"port_lane_max_merge_distance": 1000, | |||
{| class="wikitable" | {| class="wikitable" | ||
!Field | !Field | ||
Line 36: | Line 41: | ||
!Description | !Description | ||
|- | |- | ||
|shipping_lane_layers | |||
|string[] | |||
|The "shipping_lane_layers" specify what layers define the geometry for the shipping lanes. There needs to be at least one layer defined for the shipping model to work correctly, but any number of additional layers can be defined as an array. | |||
|- | |- | ||
|country_border_layer | |country_border_layer | ||
|string | |string | ||
|Layer which defines the area of each country. This was required for figuring out what country each port belongs to in order to correctly submit the KPI values. | |Layer which defines the area of each country. This was required for figuring out what country each port belongs to in order to correctly submit the KPI values. | ||
|- | |- | ||
| colspan="1" |shipping_lane_point_merge_distance | | colspan="1" |shipping_lane_point_merge_distance |
Revision as of 09:26, 11 April 2024
SEL
The Shipping simulation (SEL) can be almost completely configured within the config file for the scenario. All fields in this document are attached to the 'SEL' object of the config file, unless explicitly said otherwise.
The characteristics of SEL's pathfinding logic are explained in the conference paper listed on the Research Publications page. Something more specific to add to that paper concerns the exact configuration options with which you can control pathfinding.
Always keep in mind that SEL ideally finds straight lines between port of origin and port of destination (as ship captains in real life also would like to do), but that there are always things influencing / altering that straight line:
- Things that disallow a route from going through there, e.g. a real restriction - an island, a wind farm, a no-shipping zone, ... To configure something like this, just add the data layer as a restriction, see the first instructions explained further below under 'Restrictions'.
- Things that (strongly) encourage a route to go through there rather than just around it or slightly to the left or right from it, such as an IMO route. To configure something like this, just add the data layers as a shipping_lane_layers as explained under 'General' immediately below.
- Things that (strongly) discourage a route to go through there, e.g. shallow areas. To configure something like this, add the data layer as a restriction, and then again as a restriction exception to set a cost / penalty, as explained under 'Restrictions' too.
Common practices in e.g. the North Sea or Baltic Sea configs is to use the above system as follows:
- Make a vector version of bathymetry layer with which to discourage SEL to go through shallow waters, i.e. ...
- This means turning the bathymetry raster into a bathymetry polygon layer, based on above or below ~20m depth only.
- and then adding that new bathymetry vector layer to the config file, but making it invisible by setting layer_active, layer_selectable, layer_editable, layer_toggleable, and layer_active_on_start to 0 in the layer's definition under 'meta',
- and finally adding the layer to the 'restrictions' definition (outside of the 'SEL' object in the config file) as well as the 'restriction_exceptions' definition (back inside the 'SEL' object again). See the 'Restrictions' section further below for more information and an example.
- Make an 'invisible shipping lanes' layer with which to encourage SEL to use certain common routes that don't involve e.g. any IMO lanes or something else, i.e., ...
- a layer of lines in areas that SEL tends to avoid even though they are in real life commonly used,
- making that layer again invisible to players (see above)
- and finally adding the layer to shipping_lane_layers. Note that you can also set ship_types and then map this layer's types to those ship types using layer_type_ship_type_mapping (see below).
- Make a 'connection points' layer to help SEL find paths around areas that disallow it to use, e.g. when a port of destination is 'hiding' behind a bit of land from the perspective of the port of origin (e.g. an island or peninsula), i.e., ...
- a layer of points equally spaced, e.g. every 5 kilometers apart, horizontally and vertically, up until the shorelines, a perhaps some more going up a river
- making that layer again invisible to players (see above)
- and finally adding the layer to shipping_lane_layers. Note that you can also set ship_types and then map this layer's types to those ship types using layer_type_ship_type_mapping (see below).
- Make a 'fairways' layer to help SEL find paths up an estuary (or other land-locked, narrow bits), as an alternative for adding a big bunch of connection points (see previous point), i.e., ...
- a layer of lines, could actually be lines that are dredged in real life to ensure deep enough entrances into big ports for container or tanker vessels for example
- you could decide to keep this layer visible and editable for players. We did that with the Baltic Sea config.
- and finally adding the layer to shipping_lane_layers. Note that you can also set ship_types and then map this layer's types to those ship types using layer_type_ship_type_mapping (see below).
Base Parameters
"shipping_lane_layers": [ "NS_Shipping_Routes" ], "country_border_layer": "NS_EEZ", "shipping_lane_point_merge_distance": 10000, "shipping_lane_subdivide_distance": 50000, "shipping_lane_implicit_distance_limit": 200000, "port_lane_max_merge_distance": 1000,
Field | Type | Description |
---|---|---|
shipping_lane_layers | string[] | The "shipping_lane_layers" specify what layers define the geometry for the shipping lanes. There needs to be at least one layer defined for the shipping model to work correctly, but any number of additional layers can be defined as an array. |
country_border_layer | string | Layer which defines the area of each country. This was required for figuring out what country each port belongs to in order to correctly submit the KPI values. |
shipping_lane_point_merge_distance | double | Merge distance for simplification. All nodes on a single geometry instance will be merged if their distance to the previous node is lower than this value |
shipping_lane_subdivide_distance | double | Edges in defined geometry will be subdivided across this length so we have segments that are approximately this length. |
shipping_lane_implicit_distance_limit | double | Maximum distance we consider for creating implicit edges. |
port_lane_max_merge_distance | double | Distance at which ports can be merged / connected to shipping lane vertices. Shipping ports on land can only create new connections through this merge method. |
Ports
Definitions
"port_layers": [ { "layer_name": "BS_Ports_Model", "port_type": "DefinedPort" }, { "layer_name": "BS_Wind_Farms_Edit", "port_type": "MaintenanceDestination" } ],
Field | Type | Description |
---|---|---|
port_layers | object[] | Collection of layers that hold the shipping port geometry. |
→ layer_name | string | The layer_name field defines the name of the layer which hold the geometry for the ports. |
→ port_type | enum | Accepted Values: "DefinedPort", "MaintenanceDestination"
|
Intensities
"port_intensity": [ { "port_id": "Port0", "ship_intensity_values": [ { "start_time": 0, "ship_type_id": 0, "ship_intensity": 500 }, { "start_time": 10, "ship_type_id": 0, "ship_intensity": 1000 } ] } ]
Field | Type | Description |
---|---|---|
port_intensity | object[] | Description of the port intensities per port |
→ port_id | string | Persistent identifier of the ports we are going to specify the intensity for. |
→ ship_intensity_values | object[] | Array of intensity per ship type and per time |
→→ start_time | int | Month at which this intensity should be in full effect |
→→ ship_type_id | int | The unique ID of the ship type we are defining the intensity for |
→→ ship_intensity | int | The intensity value at the specified time and for the specified ID on the port. Intensities will be linearly interpolated in the months that are not defined. So for our example config month 5 will result in 750 intensity. Month 11 will result in 1000 intensity. |
Maintenance destinations and intensities
"maintenance_destinations": { "construction_intensity_multiplier": 2.5, "base_intensity_per_square_km": 1, "point_construction_intensity": 10, "point_intensity": 2, },
Field | Type | Description |
---|---|---|
maintenance_destinations | object | Object describing the intensities for ports that are marked with the MaintenanceDestination type |
→ construction_intensity_multiplier | float | Multiplier for intensity when a port is still in construction. (In our case this means in an Approved plan when game time is the implementation date - assembly time) |
→ base_intensity_per_square_km | float | Intensity multiplier that is applied to the square km area of the polygon. This defines the intensity of the port when the layer is fully constructed and active. |
→ point_construction_intensity | float | Intensity per month for point geometry while the geometry is sitll being constructed. |
→ point_intensity | float | Intensity per month for point geometry that are marked as maintenance destinations. |
Preconfigured Routes
"configured_routes": [ { "source_port_id": "Port1", "destination_port_id": "Port0", "ship_type_id": 4, "intensity_percentage": 1 } ]
Field | Type | Description |
---|---|---|
configured_routes | object[] | An array of preconfigured routes and their relative intensities |
→ source_port_id | string | ID of the port that the route starts |
→ destination_port_id | string | ID of the port that the route ends |
→ ship_type_id | int | ID of the ship type that should take this route |
→ intensity_percentage | float | Values 0..1. The percentage of ships that are generated on source_port that should take this route. Any ship that is not allocated via a preconfigured route will be distributed over all the other available ports depending on their intensities. Ports that have a higher intensity will be sent more ships. |
→ start_time | int | OPTIONAL: the month from when the route should become active. The simulation will always take the route that has the most recent start_time if there's multiple routes defined for the same source, destination and ship type id. |
Restrictions
The system uses the restrictions setup in the config file which intersect with the shipping lane layers. This means that SEL looks at the 'restrictions' object in the config file (so outside of the SEL section!) to find the geometry of layers mentioned in there as being restricted by layers defined under SEL's 'shipping_lane_layers'.
So for example, in the North Sea Basic config of v4.0.1, you'll find this restriction definition:
"NS_IMO_Routes|NS_Countries": [ { "message": "Shipping routes should not cross over land.", "value": 0.0, "type": "WARNING", "startlayer": "NS_IMO_Routes", "starttype": "", "endlayer": "NS_Countries", "endtype": "", "sort": "Inclusion" } ],
Notice the 'startlayer' of NS_IMO_Routes and the 'endlayer' of NS_Countries. The NS_IMO_Routes layer is defined as a shipping lane layer:
"shipping_lane_layers": [ "NS_IMO_Routes", "NS_Connection_Points_25km_2", "NS_National_shipping_lanes", "NS_Shipping_Invisible_Lanes" ],
Because of this, SEL picks up all the geometry under the NS_Countries layer and considers that as something that cannot be crossed when it tries to find the shipping paths.
Here's another example of a restriction definition in the north Sea Basic config of v4.0.1:
"NS_IMO_Routes|NS_Bat_20_shipping": [ { "message": "Bathymetry under 20m is not ideal for shipping.", "value": 0.0, "type": "INFO", "startlayer": "NS_IMO_Routes", "starttype": "", "endlayer": "NS_Bat_20_shipping", "endtype": "", "sort": "Inclusion" } ],
Again, SEL picks this up too, because NS_IMO_Routes is a shipping lane layer. So SEL also picks up all the geometry of NS_Bat_20_shipping and considers that as something that cannot be crossed when it tries to find the shipping paths.
So SEL doesn't care if the shipping lane layer in the restriction was defined under 'startlayer' or 'endlayer', and it also doesn't care about the 'type' of restriction (warning, info, error, doesn't matter), or the 'value', or 'sort'. All of that doesn't matter.
For exceptions to the shipping restriction layers and their geometry, SEL looks at the next object: 'restriction_layer_exceptions'.
Here's an example of an exception involving that same NS_Bat_20_shipping layer:
"restriction_layer_exceptions": [ { "layer_name": "NS_Bat_20_shipping", "layer_types": [ "default" ], "allowed_ships": [ { "ship_type_ids": [ 1, 2, 3, 4, 5 ], "cost_multiplier": 20.0 } ] } ]
Have a look at the definitions of the values below. As you can see, by adding NS_Bat_20_shipping as an exception for basically all ship types (IDs 1-5), all of a sudden ships are allowed into the geometry of that layer again when SEL wants to calculate its routes, except that doing so makes it expensive, so SEL will first look for alternatives before it does that. This is exactly the kind of behaviour we want when it concerns shallow waters (which is what the NS_Bat_20_shipping geometry depicts).
Field | Type | Description |
---|---|---|
restriction_layer_exceptions | object[] | Restriction layer exceptions define what exceptions there are to the restrictions to allow certain ship types to move over certain layer types. |
→ layer_name | string | The name of the layer this exception applies to |
→ layer_types | string[] | The types within the layer the exception applies to. Each corresponds to the 'displayName' of a specific layer_type of a layers as defined under 'meta' outside of the SEL config. |
→ allowed_ships | an array of allowed ships (containing the ID, and the Cost multiplier) can be created for more flexible use of the system
so Type 1,2,3 can have a multiplier of 1 and Type 4,5 can have a multiplier of 20 | |
→ →
ship_type_ids |
int[] | IDs of the ship restriction groups that should be allowed to cross over the layer and type defined. |
→ → cost_multiplier | float | Cost multiplier traveling over an edge that is affected by this exception. This can be used to make routes more costly that go over these areas while not blocking them off completely.
Defaults to 1.0. |
Heatmaps
Output Configuration
"output_configuration": { "pixels_per_mel_cell": 1, "simulation_area_cell_size": 5000 },
Field | Type | Description |
---|---|---|
output_configuration | object | Describes the raster output for SEL |
→ pixels_per_mel_cell | int | Multiplier for the amount of pixels we calculate per MEL cell. If we set this to 4 the resolution will be 4 times what MEL outputs with respect to the bound size. If the bounds of MEL and SEL (playarea) are the same SEL will output at 4x the resolution, if the bounds do not match the resolution is resized proportionally to the bounds of SEL (i.e. we take the bounds, divide the mel_cell_size by this multiplier and apply that over the width and height) |
→ simulation_area_cell_size | int | If MEL is absent you can specify the size of a pixel in the output rasters via this value. This value only applies when MEL is not configured. |
Risk heatmap
"risk_heatmap_settings": { "restriction_layer_exceptions": [ "NS_Countries" ], "shipping_intensity_risk_value": 1000 },
The risk heatmap requires some extra information to work correctly. The risk heatmap works by taking the shipping intensity of the specified ship types. This intensity map is then used as an overlay onto a map of all the specified restriction zones and the intersecting values are plotted onto the result. As an additional result any shipping value over the threshold will be drawn on the riskmap as well.
Field | Type | Description |
---|---|---|
risk_heatmap_settings | object | The "risk_heatmap_settings" block allows you to specify some additional required information for the risk heatmap. This information applies to all heatmaps and specifies which layers need to be excluded from generating the restrictions. |
→ restriction_layer_exceptions | string[] | Specifies the layers which are excluded from generating the restriction map even though they are defined in the restriction matrix. |
→ shipping_intensity_risk_value | int | From this intensity the cell should be marked as a risk zone. So any cells that have a shipping intensity that exceeds this value will be plotted on the resulting risk heatmap. |
Heatmap Configuration
"heatmap_settings": [ { "layer_name": "NS_Shipping_Intensity_shipType0", "ship_type_ids": [ 0 ], "heatmap_range": [ { "input": 0, "output": 0 }, { "input": 1000, "output": 1 } ] } ]
Field | Type | Description |
---|---|---|
heatmap_settings | object[] | Definition of all the available heatmaps for the current configuration. |
→ output_for_mel | bool | OPTIONAL: Marks this layer as a layer that should be output to MEL. This will change the raster bounds to match MEL's raster bounds and use the same resolution as MEL
Default: false |
→ heatmap_type | enum | OPTIONAL: Type of heatmap. By default this is "ShippingIntensity". Accepted values: "ShippingIntensity", "Riskmap".
Setting this to "Riskmap" will create a riskmap instead of an intensity map. |
→ layer_name | string | Name of the layer to which the output raster should be associated |
→ ship_type_ids | int[] | The IDs of the ship types that should be included in this heatmap. This can be any number of ship types IDs from just a single ship type to all ship types that are available. |
→ heatmap_range | object[] | Rangemapping of the heatmaps. This field specifies what shipping intensities should map to what colour on the heatmap. This is a sparse mapping of input and output values where the simulation will interpolate between the values where necissary. There can be any number of mapping entries in this array. |
→ → input | int | The input intensity for the mapping |
→ → output | float | Values 0..1. Output colour on the heatmap. |
"heatmap_bleed_config": { "bleed_number_of_kernels": 5, "bleed_treshold": 100, "bleed_overflow_curve_power": 0.6, "bleed_overflow_curve_multiplier": 1.25 },
Field | Type | Description |
---|---|---|
→ bleed_treshold | int | From what values the bleeding effect should start. This value value represents a maximum intensity on a single pixel. Any raster values that are over bleed_treshold bleed over to neighbouring edges.
Default: 1000 |
→ bleed_overflow_curve_power | float | The power of the the function that selects the bleeding kernel. The function for selecting the bleeding kernel is specified as: Round((x ^ bleed_overflow_curve_power) * bleed_overflow_curve_multiplier) where x = percentage of the overflow (Example: maxValue = 1, currentValue = 2, x = 2. maxValue = 1, currentValue = 3, x = 0.5).
Default: 0.6 |
→ bleed_overflow_curve_multiplier | float | The multiplier applied to the function that selects the bleeding kernel. See bleed_overflow_curve_power.
Default: 1.25 |
→ bleed_number_of_kernels | int | The number of bleeding kernels we generate. Each bleeding kernel will cause a bleed over a larger area, and the size of the bleeding is limited by the biggest kernel. The pixel kernel size is calculated by the formula (3 + (2 * kernelIndex)) and is expressed in pixels. This results in kernel the following sizes: 0 → 3x3, 1 → 5x5, 2 → 7x7, 3 → 9x9, N → (3 + (N * 2)).
Default: 15 |
Ships
Ship types
"ship_types": [ { "ship_type_id": 1, "ship_type_name": "Passenger", "ship_routing_type": "RegularShipping", "ship_agility": 0.8 } ]
Field | Type | Description |
---|---|---|
ship_types | object[] | Defines the available ship types in this scenario. |
→ ship_type_id | int | Unique integer based identifier for this specific ship type. |
→ ship_type_name | string | Name of this ship type |
→ ship_routing_type | enum | Accepted values: "RegularShipping", "Maintenance"
Default: "RegularShipping" Defines how we should populate the routing tables for this ship type.
|
→ ship_agility | float | Values 0..1. Agility defines the willingness to take shortcuts. The higher the agility the more likely the ship is to take shortcuts. Values close to 0 will take the defined shipping lanes as much as possible, and values of 1 will take the most efficient route available. |
Ship type mapping to shipping lane layer types
Use this to let SEL know which ship types are allowed over which shipping lane geometry types. In the example below, the 'Passenger' type is one of the NS_IMO_Routes layer, while the 'Invisible_Lanes' type is one of the NS_Shipping_Invisible_Lanes layer, and both layers were also defined as shipping_lane_layers.
"layer_type_ship_type_mapping": [ { "layer_type": "Passenger", "ship_type_ids": [ 1 ] }, { "layer_type": "Invisible_Lanes", "ship_type_ids": [ 1, 2, 3, 4, 5 ] } ]
Field | Type | Description |
---|---|---|
layer_type_ship_type_mapping | object[] | Maps ship types to shipping lane layer types in this scenario. Requires definition of appropriate ship types (see above). |
→ layer_type | string | Name of any of the shipping_lane_layers' types (referencing 'displayName' under the layer's 'layer_type' definition under 'meta') that should be mapped to one or more ship type IDs. For SEL to be able to find geometry with that type, make sure the layer(s) that use(s) that type have been added to the shipping_lane_layers list (see above). |
→ ship_type_ids | int[] | List of ship type IDs that should be mapped to the particular shipping_lane_layers' type (see above). |
KPIs
The calculation of KPIs is at the time of writing (v4.0.1) hard-coded within SEL. As far as I know, only the game client needs this definition, not SEL.
"shipping_kpi_config": [ { "categoryName": "General", "categoryColor": "#00FF00FF", "unit": "ship", "generateValuesPerPort": null, "categoryValueType": "Sum", "valueDefinitions": [ { "valueName": "ShippingIntensity", "valueColor": "#00FF00FF", "unit": null, "valueDependentCountry": 0 }, { "valueName": "ShippingRisk", "valueColor": "#00FF00FF", "unit": null, "valueDependentCountry": 0 } ] }, { "categoryName": "Route Efficiency", "categoryColor": "#00FFFFFF", "unit": "%", "generateValuesPerPort": "ShippingRouteEfficiency_", "categoryValueType": "Average", "valueDefinitions": null } ]
Field | Type | Description |
---|---|---|
shipping_kpi_config | object[] | Defines the shipping key performance indicators (KPIs) that should be calculated and stored after each month run for this scenario. |
→ categoryName | string | Name of the category of KPIs you are defining, used by the client for grouping. |
→ categoryColor | string | Hexadecimal colour code (with the last two digits representing transparency) for said category, used by the client when KPIs under this category are selected for graph drawing. |
→ unit | string | Unit of measurement that should be shown next to the actual KPI value. Currently not used as SEL has hard-coded units of measurement, and in turn the client gets this from the session database (and thus through the websocket service). |
→ generateValuesPerPort | string | Nullable. If not null, then this indicates that the KPI should be calculated per port, as defined under port_layers. Currently not used as SEL has hard-coded KPI calculations. |
→ categoryValueType | string | Stipulates how to calculate the KPI in question. Currently this has been set to Sum or Average. The SEL code doesn't do anything with this currently. |
→ valueDefinitions | object[] | Any specific value groupings for a particular KPI category. Currently only in use for the general (so non-port-specific) KPIs. |
→→ valueName | string | Name of the KPI value falling under this category, used by the client to know what KPI to actually pick up and show. |
→→ valueColor | string | Hexadecimal colour code (with the last two digits representing transparency) for said value of the KPI falling under the category at hand, used by the client when this KPI is selected for graph drawing. |
→→ unit | string | Nullable. If not null, then it overrides the category wide unit of measurement. (untested) |
→→ valueDependentCountry | int | 0 or the country ID of a specific country defined as one of the types of the layer defined under 'countries' (thus representing the countries data layer). Currently not implemented, as far as I can tell. |