vignettes/package_networks.Rmd
package_networks.Rmd
This vignette is currently a work in progress and changes are
likely (20/6/22)
This Vignette looks at the pyrosm, OSMnx,
and osmsxtract packages for obtaining network data from OpenStreetMaps.
Here, we look at how each package defines one of three
pre-defined networks:
With OpenInfra’s specific focus on active travel and
infrastructure our focus is on Cycling and Walking networks.
Driving networks have been included for completeness.
Understanding how such packages define networks will allow us to analyse
and combine the tag filters used by each package to define, hopefully,
the best defined active travel networks.
Firstly we go through the osmextract (R) package function
documentation to see which filters are applied to each of the three
network types.
osmextract creates networks with the following
workflow. A place name string (i.e. “Leeds”) is matched to one of two
dataset providers, BBBike or Geofabrik before downloading a
compressed .pbf
file of OSM data for the specified place (Leeds). Next the downloaded
file is converted into the open standard .gpkg
format. This
file is then parsed with an SQL query to obtain network features
specified by the user (i.e. “walking”, “cycling”, or “driving”).
Here we look at the three pre-defined filters offered by
osmextract
and how these are parsed with an SQL query, we
then try to translate these queries into a python format, similar to the
filtering usilised by pyrosm/OSMnx.
After, we compare the tag
filtering applied by each package to obtain our final networks.
Note that as the osmextract
package defines the osm layer
it is looking at (layer="lines"
) as being the lines (OSM
ways - roads, footpaths, etc.) then we do not need to specify area=“no”,
as by default, no areas (polygons) are returned within the
"lines"
layer.
# A motorcar/motorcycle mode of transport includes the following scenarios:
# - highway IS NOT NULL (since usually that means that's not a road);
# - highway NOT IN ('bus_guideway', 'byway' (not sure what it means), 'construction',
# 'corridor', 'cycleway', 'elevator', 'fixme', 'footway', 'gallop', 'historic',
# 'no', 'pedestrian', 'platform', 'proposed', 'steps', 'pedestrian',
# 'bridleway', 'path', 'platform');
# - access NOT IN ('private', 'no');
# - service NOT ILIKE 'private%';
load_options_driving = function(place) {
list(
place = place,
layer = "lines",
extra_tags = c("access", "service"),
vectortranslate_options = c(
"-where", "
(highway IS NOT NULL)
AND
(highway NOT IN (
'abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator',
'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform',
'proposed', 'cycleway', 'pedestrian', 'bridleway', 'path', 'footway',
'steps'
))
AND
(access NOT IN ('private', 'no'))
AND
(service NOT ILIKE 'private%')
"
)
)
}
Features must have a valid value for highway tag [i.e. highway!=None] (highway IS NOT NULL)
Features are removed if highway tag = any of the following values:
('abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator',
'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform',
'proposed', 'cycleway', 'pedestrian', 'bridleway', 'path', 'footway',
'steps')
Features are removed if access tag = any of the following values: ("private", "no")
Features are removed if the service tag value (a string) contains the sub-string ("private")
osmextract_filter = dict()
osmextract_filter["driving"] = (
f'["highway]'
f'["highway!~"abandoned|bus_guideway|byway|construction|corridor|elevator|fixme|escalator|gallop|historic|no|'
f'planned|platform|proposed|cycleway|pedestrian|bridleway|path|footway|steps"]'
f'["access"!~"private|no"]'
f'["service"!~"private"]' #Not too sure how to represent this
)
# Note that the bottom line (f'["service"!~"private"]') should be any value for the service tag should not contain the pattern "private", no matter the actual whole string
# See also https://wiki.openstreetmap.org/wiki/Key:footway and
# https://wiki.openstreetmap.org/wiki/Key:foot
# A walking mode of transport includes the following scenarios:
# - highway IS NOT NULL (since usually that means that's not a road);
# - highway NOT IN ('abandoned', 'bus_guideway', 'byway', 'construction', 'corridor',
# 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned',
# 'platform', 'proposed', 'raceway', 'motorway', 'motorway_link');
# - highway != 'cycleway' OR foot IN ('yes', 'designated', 'permissive', 'destination');
# - access NOT IN ('no', 'private');
# - foot NOT IN ('no', 'use_sidepath', 'private', 'restricted')
# - service does not look like 'private' (ILIKE is string matching case insensitive)
load_options_walking = function(place) {
list(
place = place,
layer = "lines",
extra_tags = c("access", "foot", "service"),
vectortranslate_options = c(
"-where", "
(highway IS NOT NULL)
AND
(highway NOT IN ('abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator',
'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'raceway',
'motorway', 'motorway_link'))
AND
(highway <> 'cycleway' OR foot IN ('yes', 'designated', 'permissive', 'destination'))
AND
(access NOT IN ('private', 'no'))
AND
(foot NOT IN ('private', 'no', 'use_sidepath', 'restricted'))
AND
(service NOT ILIKE 'private%')
"
)
)
}
Features must have a valid value for highway tag [i.e. highway!=None] (highway IS NOT NULL)
Features are removed if they contain highway tag = any of the following values:
('abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator',
'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'raceway',
'motorway', 'motorway_link')
Features are removed if they contain highway tag = ("cycleway") UNLESS these features contain the tag:value
combination of foot = ("yes", "designated", "permissive", "destination") (i.e this cycleway IS pedestrian friendly too)
Features are removed if they contain access tag = ("private", "no")
Features are removed if they contain foot tag = ("private", no", "use_sidepath", "restricted")
Features are removed if the service tag value (a string) contains the sub-string ("private")
osmextract_filter = dict()
osmextract_filter["walking"] = (
f'["highway"]'
f'["highway"!~"abandoned|bus_guideway|byway|construction|corridor|elevator|fixme|'
f'escalator|gallop|historic|no|planned|platform|proposed|raceway|motorway|motorway_link"]'
f'["highway!~"cycleway"] UNLESS ["foot"~"yes|designated|permissive|destination"]' # I know this is not proper python. will update once I know more.
f'["access"!~"private|no"]'
f'["foot"!~"private|no|use_sidepath|restricted"]'
f'["service"!~"private"]' # Not too sure how to represent this
)
# A cycling mode of transport includes the following scenarios:
# - highway IS NOT NULL (since usually that means that's not a road);
# - highway NOT IN ('abandoned', 'bus_guideway', 'byway', 'construction', 'corridor',
# 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned',
# 'platform', 'proposed', 'raceway', 'steps');
# - highway NOT IN IN ('motorway', 'motorway_link', 'bridleway', 'footway',
# 'pedestrian) OR bicycle IN ('yes', 'designated', 'permissive', 'destination');
# - access NOT IN ('no', 'private');
# - bicycle NOT IN ('no', 'use_sidepath')
# - service does not look like 'private' (ILIKE is string matching case insensitive)
load_options_cycling = function(place) {
list(
place = place,
layer = "lines",
extra_tags = c("access", "bicycle", "service"),
vectortranslate_options = c(
"-where", "
(highway IS NOT NULL)
AND
(highway NOT IN (
'abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator',
'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform',
'proposed', 'raceway', 'steps'
))
AND
(highway NOT IN ('motorway', 'motorway_link', 'footway', 'bridleway',
'pedestrian') OR bicycle IN ('yes', 'designated', 'permissive', 'destination')
)
AND
(access NOT IN ('private', 'no'))
AND
(bicycle NOT IN ('private', 'no', 'use_sidepath', 'restricted'))
AND
(service NOT ILIKE 'private%')
"
)
)
}
Features must have a valid value for highway tag [i.e. highway!=None] (highway IS NOT NULL)
Features are removed if they contain highway tag = any of the following values:
('abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator',
'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform',
'proposed', 'raceway', 'steps')
Features are removed if they contain highway tag = ("motorway", "motorway_link", "footway", "bridleway") UNLESS these
features ALSO contain the tag:value combination of bicycle = ("yes", "designated", "permissive", "destination")
Features are removed if they contain the tag access = ("private", "no")
Features are removed if they contain the tag bicycle = ("private", "no", "use_sidepath", "restricted")
Features are removed if the service tag value (a string) contains the sub-string ("private") [service NOT ILIKE 'private%']
osmextract_filter = dict()
osmextract_filter["cycling"] = (
f'["highway"]'
f'["highway"!~"abandoned|bus_guideway|byway|construction|corridor|elevator|fixme|escalator|gallop'
f'historic|no|planned|platform|proposed|raceway|steps"]'
f'["highway"!~"motorway|motorway_link|footway|bridleway|pedestrian] UNLESS ["bicycle"~"yes|designated|permissive|destination"]' # I know this is not correct Python - looking into this (i.e. A unless B)
f'["access"!~"private|no"]'
f'["bicycle"!~"private|no|use_sidepath|restricted"]'
f'["service"!~"private"]' # Not sure how to represent in Python (grepl in R)
)
The pyrosm package defines networks by first taking a complete
network from OSM. It then recursively removes features that contain tags
matching specific networks described below:
Definitions have
been obtained from pyrosm
source code, the OSMnx
source code, and the osmextract
source code.
It should be noted that for all OSMnx OSM API
queries the “highway” tag is specified.
specifying
way[“highway”] means that all ways returned must have a highway tag. the
specific filters then remove ways by tag/value.
Cycling Filter removed tags
Tag | Pyrosm Values | OSMnx Values | osmextract | Proposed Filter |
---|---|---|---|---|
area | “yes” | “yes” | None | N/A |
access | None | “private” | “private”, “no” | “private”, “no” |
highway | “footway”, “steps”, “corridor”, “elevator”, “escalator”, “motor”, “proposed”, “construction”, “abandoned”, “platform”, “raceway”, “motorway”, “motorway_link” | “abandoned”, “bus_guideway”, “construction”, “corridor”, “elevator”, “escalator”, “footway”, “motor”, “planned”, “platform”, “proposed”, “raceway”, “steps” | ‘abandoned’, ‘bus_guideway’, ‘byway’, ‘construction’, ‘corridor’, ‘elevator’, ‘fixme’, ‘escalator’, ‘gallop’, ‘historic’, ‘no’, ‘planned’, ‘platform’, ‘proposed’, ‘raceway’, ‘steps’ | ‘abandoned’, ‘bus_guideway’, ‘byway’, ‘construction’, ‘corridor’, ‘elevator’, ‘fixme’, ‘escalator’, ‘gallop’, ‘historic’, ‘no’, ‘planned’, ‘platform’, ‘proposed’, ‘raceway’, ‘steps’, “motor” |
highway | not needed | not needed | Remove feature if highway = (“motorway”, “motorway_link”, “footway”, “bridleway”) UNLESS bicycle = (“yes”, “designated”, “permissive”, “destination”) | Remove if highway= (“motorway”, “motorway_link”, “footway”, “bridleway”) UNLESS bicycle = (“yes”, “designated”, “permissive”, “destination) |
bicycle | “no” | “no” | “private”, “no”, “use_sidepath”, “restricted” | “private”, “no”, “use_sidepath”, “restricted” |
service | “private” | “private” | “[str for str in values if ”private” in str]”, (in other words if ‘private’ is in a value for this tag, remove this feature) where values = network[‘service’].keys() | remove if key value contains substring “private” |
Driving Filter removed tags
Tag | Pyrosm Values | OSMnx Values | osmextract | Proposed Filter |
---|---|---|---|---|
area | “yes” | “yes” | None | N/A |
access | None | “private” | “private”, “no” | “private”, “no” |
highway | “cycleway”, “footway”, “path”, “pedestrian”, “steps”, “track”, “corridor”, “elevator”, “escalator”, “proposed”, “construction”, “bridleway”, “abandoned”, “platform”, “raceway” | “abandoned”, “bridleway”, “bus_guideway”, “construction”, “corridor”, “cycleway”, “elevator”, “escalator”, “footway”, “path”, “pedestrian”, “planned”, “platform”, “proposed”, “raceway”, “service”, “steps”, “track” | ‘abandoned’, ‘bus_guideway’, ‘byway’, ‘construction’, ‘corridor’, ‘elevator’, ‘fixme’, ‘escalator’, ‘gallop’, ‘historic’, ‘no’, ‘planned’, ‘platform’, ‘proposed’, ‘cycleway’, ‘pedestrian’, ‘bridleway’, ‘path’, ‘footway’, ‘steps’ | ‘abandoned’, ‘bus_guideway’, ‘byway’, ‘construction’, ‘corridor’, ‘elevator’, ‘fixme’, ‘escalator’, ‘gallop’, ‘historic’, ‘no’, ‘planned’, ‘platform’, ‘proposed’, ‘cycleway’, ‘pedestrian’, ‘bridleway’, ‘path’, ‘footway’, ‘steps’, “track”, “raceway”, “service” |
motor_vehicle | “no” | “no” | None | “no” |
motorcar | “no” | “no” | None | “no” |
service | “parking”, “parking_aisle”, “private”, “emergency_access” | “alley”, “driveway”, “emergency_access”, “parking”, “parking_aisle”, “private” | “[str for str in values if ”private” in str]”, (in other words if ‘private’ is in a value for this tag, remove this feature) where values = network[‘service’].keys() | “alley”, “driveway”, “emergency_access”, “parking”, “parking_aisle”, “private”/[str for str in values if ”private” in str] |
Driving & Service Filter removed
tags
Tag | Pyrosm Values | OSMnx Values | osmextract |
---|---|---|---|
access | None | “yes” | unspecified |
area | “yes” | “yes” | unspecified |
highway | “cycleway”, “footway”, “path”, “pedestrian”, “steps”, “track”, “corridor”, “elevator”, “escalator”, “proposed”, “construction”, “bridleway”, “abandoned”, “platform”, “raceway” | “abandoned”, “bridleway”, “bus_guideway”, “construction”, “corridor”, “cycleway”, “elevator”, “escalator”, “footway”, “path”, “pedestrian”, “planned”, “platform”, “proposed”, “raceway”, “steps”, “track” | unspecified |
motor_vehicle | “no” | “no” | unspecified |
motorcar | “no” | “no” | unspecified |
service | “parking”, “parking_isle”, “private”, “emergency_access” | “emergency_access”, “parking”, “parking_aisle”, “private” | unspecified |
psv | “yes” | None | unspecified |
Walking Filter removed tags
Tag | Pyrosm Values | OSMnx Values | osmextract | Proposed Filter |
---|---|---|---|---|
access | None | “private” | “private”, “no” | “private”,“no” |
area | “yes” | “yes” | None | N/A |
highway | “cycleway”, “motor”, “proposed”, “construction”, “abandoned”, “platform”, “raceway”, “motorway”, “motorway_link” | “abandoned”, “bus_guideway”, “construction”, “cycleway”, “motor”, “planned”, “platform”, “proposed”, “raceway” | ‘abandoned’, ‘bus_guideway’, ‘byway’, ‘construction’, ‘corridor’, ‘elevator’, ‘fixme’, ‘escalator’, ‘gallop’, ‘historic’, ‘no’, ‘planned’, ‘platform’, ‘proposed’, ‘raceway’, ‘motorway’, ‘motorway_link’ | ‘abandoned’, ‘bus_guideway’, ‘byway’, ‘construction’, ‘corridor’, ‘elevator’, ‘fixme’, ‘escalator’, ‘gallop’, ‘historic’, ‘no’, ‘planned’, ‘platform’, ‘proposed’, ‘raceway’, ‘motorway’, ‘motorway_link’, ‘motor’ |
highway | not needed | not needed | Remove feature if highway = “cycleway” UNLESS feature also has foot = “yes”, “permissive”, “designated”, “destination” | remove feature if highway=cycleway UNLESS feature has foot=(“yes”, “permissive”, “designated”, “destination”) |
foot | “no” | “no” | “private”, “no”, “use_sidepath”, “restricted” | “no”, “private”, “use_sideapth”, “restricted” |
service | “private” | “private” | “[str for str in values if ”private” in str]”, (in other words if ‘private’ is in a value for this tag, remove this feature) where values = network[‘service’].keys() | “[str for str in values if ”private” in str]” |
.
Here we show the code used to determine the final combination of
proposed tag filters to use as our proposed filters within the tables
above.
This was done by creating a set of each packages tags
filter and finding the setdifference and combinging this, for each
network type. See below:
# Driving Filter (highway tag)
pyrosm_set_drive = set(["cycleway", "footway", "path", "pedestrian", "steps", "track", "corridor", "elevator", "escalator", "proposed", "construction", "bridleway", "abandoned", "platform", "raceway"])
OSMnx_set_drive = set(["abandoned", "bridleway", "bus_guideway", "construction", "corridor", "cycleway", "elevator", "escalator", "footway", "path", "pedestrian", "planned", "platform", "proposed", "raceway", "service", "steps", "track"])
osmextract_set_drive = set(['abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'cycleway', 'pedestrian', 'bridleway', 'path', 'footway', 'steps'])
proposed_set_drive = set(['abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'cycleway', 'pedestrian', 'bridleway', 'path', 'footway', 'steps', "track", "raceway", "service"])
# Things in osmextract filter, not in OSMnx/pyrosm filters
print(osmextract_set_drive.difference(pyrosm_set_drive))
##>{'historic', 'no', 'gallop', 'planned', 'fixme', 'bus_guideway', 'byway'}
# Things in osmextract filter, not in OSMnx/pyrosm filters
print(osmextract_set_drive.difference(OSMnx_set_drive))
##> {'historic', 'no', 'gallop', 'fixme', 'byway'}
# Things in OSMnx/pyrosm filters, not in osmextract
print(OSMnx_set_drive.difference(osmextract_set_drive))
##> {'raceway', 'service', 'track'}
# Things in OSMnx/pyrosm filters, not in osmextract
print(pyrosm_set_drive.difference(osmextract_set_drive))
##> {'raceway', 'track'}
The proposed filter is the osmextract
filter with “track”, “raceway”, “service” keys added.
pyrosm_cycle = set(["footway", "steps", "corridor", "elevator", "escalator", "motor", "proposed", "construction", "abandoned", "platform", "raceway", "motorway", "motorway_link"])
OSMnx_cycle = set(["abandoned", "bus_guideway", "construction", "corridor", "elevator", "escalator", "footway", "motor", "planned", "platform", "proposed", "raceway", "steps"])
osmextract_cycle = set(['abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'raceway', 'steps'])
osmextract_UNLESS_cycle = set(["motorway", "motorway_link", "footway", "bridleway"])
osmextract_combined = set(['abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'raceway', 'steps', "motorway", "motorway_link", "footway", "bridleway"])
# Tags in osmextract filter not in OSMnx/pyrosm filters
print(osmextract_combined.difference(OSMnx_cycle))
##> {'historic', 'no', 'gallop', 'bridleway', 'fixme', 'motorway_link', 'motorway', 'byway'}
# Tags in osmextract filter not in OSMnx/pyrosm filters
print(osmextract_combined.difference(pyrosm_cycle))
##> {'historic', 'no', 'gallop', 'planned', 'bridleway', 'fixme', 'bus_guideway', 'byway'}
#Tags in OSMnx/pyrosm filters not in osmextract filters
print(OSMnx_cycle.difference(osmextract_combined))
##> {'motor'}
#Tags in OSMnx/pyrosm filters not in osmextract filters
print(pyrosm_cycle.difference(osmextract_combined))
##> {'motor'}
The proposed filter is the osmextract
filter with “motor” key added.
Note this will still include the
remove feature if highway=[“motorway”, “motorway_link”, “footway”,
“bridleway”] UNLESS bicycle=[“yes”, “designated”, “permissive”,
“destination”] clause.
#Walking Filter
pyrosm_walk = set(["cycleway", "motor", "proposed", "construction", "abandoned", "platform", "raceway", "motorway", "motorway_link"])
OSMnx_walk = set(["abandoned", "bus_guideway", "construction", "cycleway", "motor", "planned", "platform", "proposed", "raceway"])
osmextract_walk = set(['abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'raceway', 'motorway', 'motorway_link'])
osmextract_UNLESS_walk = set("cycleway")
osmextract_combined = set(['abandoned', 'bus_guideway', 'byway', 'construction', 'corridor', 'elevator', 'fixme', 'escalator', 'gallop', 'historic', 'no', 'planned', 'platform', 'proposed', 'raceway', 'motorway', 'motorway_link', "cycleway"])
# Comparing OSMnx & Pyrosm filter
print(pyrosm_walk.difference(OSMnx_walk))
##> {'motorway', 'motorway_link'}
# Comparing OSMnx & Pyrosm filter
print(OSMnx_walk.difference(pyrosm_walk))
##> {'bus_guideway', 'planned'}
# Update pyrosm/OSMnx filters to match
pyrosm_walk.update(["bus_guideway", "planned"])
OSMnx_walk.update(["motorway_link", "motorway"])
# Comparing updated filters
print(pyrosm_walk.difference(OSMnx_walk))
##> set()
# Comparing updated filters
print(OSMnx_walk.difference(pyrosm_walk))
##> set()
# Comparing difference to osmextract filter
print(OSMnx_walk.difference(osmextract_walk))
##> {'motor', 'cycleway'}
# Comparing difference to osmextract filter
print(pyrosm_walk.difference(osmextract_walk))
##> {'motor', 'cycleway'}
We can see that the osmnextract filter is missing both
“motor” and “cycleway”.
For the proposed filter we will add
“motor” but not cycleway, as this will be taken care of by the
proceeding highway=cycleway UNLESS foot=(“yes”, “designated”,
“permissive”, “destination”) clause.
From a pyrosm package pull request it
has been proposed to add
highway=“busway” to the removed features list.
Additionaly, it
was propsed to also use bicycle=“use_sidepath”, however this is already
covered by the osmextract package.
I am looking into how each package (osmextract, pyrosm, OSMnx)
acquires OSM data so that we may use the most appropriate method moving
forward.
Pyrosm collects pre-formated OSM extracts by custom
user query from one of two providers, BBBike or Geofabrik.
It
appears that Pyrosm takes all data for a user place query, before
iteratively removing features from the parsed .osm.pbf file, in a
top-down approach. If features do not satisfy the filter conditions
specified by the filter
documentation then they are removed. Features are only removed from
the downloaded file if they are caught by the pyrosm filtering,
suggesting that some incorrect (unknown) features may still
remain in the network if they are not caught by this filter.
On
the other hand, it seems that osmextract also downloads data from
providers (Geofabrik & BBBike) before then filtering data using a
bottom up apprpach.
From the downloaded .pbf
file
which is then converted to the open .gpkg
format,
osmextract
then parses the .gpkg
file using
SQL queries. The networks features returned from such a request will
only be returned if they satisfy the SQL query passed, and as such only
known features should be returned, unlike pyrosm which may contain
unknown features.
From studying the OSMnx documentation, it
seems this may take a similar approach to osmextract making direct
queries to the OSM API but I need to study this further.
I
propose that we proceed utilizing the osmextract library, making use of
the SQL query option so that we can specifically define queries
(i.e. highway=cycleway UNLESS foot=yes, permissive, designated,
destination etc…)