pyrosm.OSM.write_pbf#
- OSM.write_pbf(data, output_path, subset_only=False, *, apply_geometry=False, delete=None, on_orphan_node='remove', on_deleted_member='drop', progress=None)#
Write the OSM data this object holds back to a valid, re-readable
*.osm.pbf, applying attribute/tag edits from a (modified) GeoDataFrame.The whole cached dataset (all nodes/ways/relations read from the source) is written; for a read with a
bounding_box, its nodes and ways and the relations with a member among them, and withcomplete_relations=Truealso the member ways outside the box and their nodes. The tag columns of a row (and its JSONtags) are the tags written for the matching element (byosm_type+id), so a tag whose column is left out of the frame is not written; rows whoseidis not in the source are added as new elements synthesized from their geometry (with negative ids). A frame without tag columns (onlyid,osm_type, the geometry and other structural columns) changes no tags: the elements it matches keep their own. The columnsget_networkcomputes (u,vand the numericlength) are not tags, andid_tag, the column of a tag keyedid, is written asid. An element in several rows, such as a way split into segments byget_network(nodes=True)or an element in two frames, gets each tag from its rows: a value changed or removed in only some of them is written for the whole element (with aUserWarningwhen other rows of the same frame keep the old value), and rows giving a tag different new values raiseValueError. When elements lose the tag their read selected them by, e.g. buildings written without abuildingcolumn, aUserWarningsays how many and which reader will no longer find them in the written file. Topology and coordinates come from the data pyrosm read, so the output is faithful and re-readable (e.g. by pyrosm, osmium, GDAL and r5py/R5). The elements are written sorted by type then id, new ones included, with the version, timestamp and changeset they were read with (none for nodes read withkeep_metadata=False); the file is written next tooutput_pathand replaces it only when it is complete.Pass
subset_only=Trueto instead write a PBF containing only the elements indata(matched byosm_type+id) plus the references they need to stay valid – e.g. export just the buildings or just the road network to a new file. Multiple layers can be combined by passing a list ([buildings, network]); the union of their elements is written.Typical use is to modify attributes in pandas and save them back (tags are text, so a number is read from them first):
import pandas as pd osm = OSM("data.osm.pbf") edges = osm.get_network("driving") edges["maxspeed"] = edges["maxspeed"].fillna("50") speed = pd.to_numeric(edges["maxspeed"], errors="coerce") # km/h edges["travel_time"] = edges["length"] / (speed / 3.6) osm.write_pbf(edges, "modified.osm.pbf")
Pass
apply_geometry=Trueand/ordelete=to edit the geometry itself. Geometry follows the OSM model: coordinates live on nodes, and a way is an ordered list of member node ids. So moving a node moves every way that references it, and an existing way is reshaped through itsnodescolumn (construct theOSMobject withkeep_node_info=Trueto get it) rather than through its LineString, which is derived:osm = OSM("data.osm.pbf", keep_node_info=True) nodes, edges = osm.get_network("driving", nodes=True) # Move an existing node; the ways through it follow. nodes.loc[nodes["id"] == 12345, "geometry"] = Point(24.94, 60.17) # Insert a new vertex into a way, referencing a node added in the same call. new_node = gpd.GeoDataFrame( {"id": [-1], "osm_type": ["node"]}, geometry=[Point(24.95, 60.18)], crs="EPSG:4326", ) row = edges["id"] == 4732994 edges.loc[row, "nodes"] = edges.loc[row, "nodes"].apply( lambda ids: ids[:2] + [-1] + ids[2:] ) osm.write_pbf( [nodes, edges, new_node], "edited.osm.pbf", apply_geometry=True, delete=[("way", 4732995)], )
- Parameters:
data (GeoDataFrame or list of GeoDataFrame) – The (possibly modified) feature frame(s) whose tag columns are written onto the matching elements; a frame with no tag columns leaves their tags as they are. New rows (ids not in the source) are added from their geometry:
Point-> node,LineString-> way, hole-lessPolygon-> closed way. Polygons with holes, MultiPolygon and MultiLineString geometries are not supported and raiseValueError.output_path (str) – Where to write the PBF.
subset_only (bool (default False)) – If True, write only the elements present in
data(and the nodes and relation members they reference) instead of the whole cached dataset.apply_geometry (bool (default False)) –
If True, also apply the rows’ geometry to the elements that already exist in the source, and honour caller-chosen ids for new ones:
a node row’s
Pointmoves that node to those coordinates;a way row’s
nodescolumn replaces that way’s member node ids, which covers inserting, removing and reordering vertices (the way’s own LineString is ignored – it is derived from the nodes, and underget_network(nodes=True)it is a single segment of the way);a row with a negative id that is not in the source is added under exactly that id, instead of a generated one, so a way’s
nodeslist can reference a vertex added in the same call.
Removing a vertex from a way’s
nodesdoes not delete the node itself; usedeletefor that. When False (default), rows only edit tags and add new elements, exactly as before.delete (iterable of (osm_type, id), optional) – Elements to omit from the output, e.g.
[("way", 4732994), ("node", 277446336)]. An id that is not in the source raisesValueError; an element that is both deleted and present as a row indatais deleted. Deleting a relation drops only the relation – its members are independent elements and survive.on_orphan_node ({'remove', 'keep', 'error'} (default 'remove')) – What to do with a node that was a member of a dropped way and is left referenced by no kept way or relation:
'remove'drops it too (tagged nodes included),'keep'writes it as a bare node,'error'raises. Nodes that no dropped way referenced – standalone POIs, for instance – are never touched.on_deleted_member ({'drop', 'error'} (default 'drop')) – What to do when a kept way or relation still references a deleted element:
'drop'removes it from that member list,'error'raises. A way left with fewer than two members cannot form a geometry, so it is not written either (with aUserWarning) and its own members go throughon_orphan_node; a relation left with no members is still written.progress (bool | callable | None (default: None)) – How the write shows its progress, as
progressofOSMdoes (Nonefollows theprogressof this object): one bar “Writing <file>” from 0 to 100 % over the whole write, its current step in the label, including the read of the file when it is not read in memory yet, orprogress(done, 100)calls.
- Returns:
The path of the written PBF file.
- Return type:
str
Notes
Elements not present in
datakeep their ids, tags, coordinates and member order, so the output re-reads identically in pyrosm, osmium/pyosmium, GDAL and r5py/R5. Edits never introduce a reference to a node that is not in the output; a source way whose members already point outside a clipped extract is re-emitted as it was read.