Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for modifying relations based on object metadata #16

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions changegen/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,27 @@ def _get_db_tables(suffix, dbname, dbport, dbuser, dbpass, dbhost):
),
default="2000",
)
@click.option(
"--modify_relations",
is_flag=True,
help=(
"Add new objects to parent relations specified by a special tag. "
'The default tag prefix for tags containing Relation IDs is "_member_of". '
"Pass the --relation_tag flag to change "
"this prefix, e.g. --relation_tag __a_different_prefix_. "
"See changegen.relations.py for more information. "
),
)
@click.option(
"--relation_tag",
is_flag=False,
help=(
"Only used with --modify_relations. Specify the tag prefix "
"used to search for IDs to to add new OSM objects to."
),
default="_member_of",
show_default=True,
)
@click.option("--osmsrc", help="Source OSM PBF File path", required=True)
@click.argument("dbname", default=os.environ.get("PGDATABASE", "conflate"))
@click.argument("dbport", default=os.environ.get("PGPORT", "15432"))
Expand Down Expand Up @@ -233,6 +254,8 @@ def main(*args: tuple, **kwargs: dict):
self_intersections=kwargs["self"],
max_nodes_per_way=int(max_nodes_per_way),
modify_only=kwargs["modify_meta"],
modify_relations=kwargs["modify_relations"],
relation_tag=kwargs["relation_tag"],
)

for table in kwargs["deletions"]:
Expand Down
12 changes: 11 additions & 1 deletion changegen/changewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,18 @@ class OSMChangeWriter(object):
)
_root_element_close = "</osmChange>"

def __init__(self, filename=None, compress=False):
def __init__(self, filename=None, compress=False, keepcopy=False):
super(OSMChangeWriter, self).__init__()

self.compress = compress
self.filename = filename
self.fileobj = None
self.closed = False
self._data_written = False
self.keepcopy = keepcopy
self.created = []
self.modified = []
self.deleted = []

# set fileobj based on compression
if self.filename and self.compress:
Expand Down Expand Up @@ -163,6 +167,8 @@ def add_modify(self, elementlist):
write_osm_object(e, writer)
writer.flush()
self._data_written = True
if self.keepcopy:
self.modified.extend(elementlist)

def add_create(self, elementlist):
"""Creates <create> element containing
Expand All @@ -178,6 +184,8 @@ def add_create(self, elementlist):
write_osm_object(e, writer)
writer.flush()
self._data_written = True
if self.keepcopy:
self.created.extend(elementlist)

def add_delete(self, elementlist):
"""Creates a <delete> element containing
Expand All @@ -189,3 +197,5 @@ def add_delete(self, elementlist):
write_osm_object(e, writer)
writer.flush()
self._data_written = True
if self.keepcopy:
self.deleted.extend(elementlist)
96 changes: 83 additions & 13 deletions changegen/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .changewriter import Tag
from .changewriter import Way
from .db import OGRDBReader
from .relations import RelationUpdater

WGS84 = pyproj.CRS("EPSG:4326")
WEBMERC = pyproj.CRS("EPSG:3857")
Expand Down Expand Up @@ -483,6 +484,9 @@ def generate_changes(
self_intersections=False,
max_nodes_per_way=2000,
modify_only=False,
modify_relations=False,
relation_tag="_member_of",
relation_insertion_tag="parent_osm_id",
):
"""
Generate an osm changefile (outfile) based on features in <table>
Expand Down Expand Up @@ -514,7 +518,11 @@ def generate_changes(
others = [others] if isinstance(others, str) else others

db_reader = OGRDBReader(dbname, dbport, dbuser, dbpass, dbhost)
change_writer = OSMChangeWriter(outfile, compress=compress)
# keep a copy of elements if we modify_relations
# becasue we need them later
change_writer = OSMChangeWriter(
outfile, compress=compress, keepcopy=modify_relations
)

new_feature_iter = db_reader.get_layer_iter(table)
layer_fields = db_reader.get_layer_fields(table)
Expand Down Expand Up @@ -573,17 +581,32 @@ def generate_changes(
):
raise NotImplementedError("Multi geometries not supported.")
if isinstance(wgs84_geom, sg.LineString):
ways, nodes = _generate_ways_and_nodes(
wgs84_geom,
ids,
feat_tags,
intersection_db,
max_nodes_per_way=max_nodes_per_way,
)
new_nodes.extend(nodes)
new_ways.extend(ways)
_global_node_id_all_ways.extend(chain.from_iterable([w.nds for w in ways]))
if isinstance(wgs84_geom, sg.Polygon):
## NOTE that modify_only does not support modifying geometries.
if modify_only:
existing_id = feature.GetFieldAsString(feature.GetFieldIndex("osm_id"))

new_ways.append(
Way(
id=existing_id,
version=2,
nds=existing_nodes_for_ways[existing_id],
tags=[tag for tag in feat_tags if tag.key != "osm_id"],
)
)
else: # not modifying, just creating
ways, nodes = _generate_ways_and_nodes(
wgs84_geom,
ids,
feat_tags,
intersection_db,
max_nodes_per_way=max_nodes_per_way,
)
new_nodes.extend(nodes)
new_ways.extend(ways)
_global_node_id_all_ways.extend(
chain.from_iterable([w.nds for w in ways])
)
elif isinstance(wgs84_geom, sg.Polygon):
## If we're taking all features to be newly-created (~modify_only)
## we need to create ways and nodes for that feature.
## IF we're only modifying existing features with features
Expand Down Expand Up @@ -685,6 +708,53 @@ def generate_changes(
if len(new_relations) > 0:
change_writer.add_create(new_relations)

## Relation Updates: If modify_relations is true,
## we'll search through all newly-added objects
## for Tags with prefix specified by `relation_tag`
## and add them to the relations specified by the
## values of those tags.
modified_relations = []
if modify_relations:
updater = RelationUpdater()
relations_mentioned = set()
for obj in change_writer.created:
relations_mentioned.update(
chain.from_iterable(
[
_t.value.split(",")
for _t in obj.tags
if _t.key.startswith(relation_tag)
]
)
)
# create relations DB
updater.get_relations(relations_mentioned, osmsrc)
# update db for each new object
for obj in tqdm(
change_writer.created,
desc="Checking objects for relations...",
):
# check to see if a tag that matches `relation_insertion_tag`
# is present, and if so, provide the value as`
# at_id to insert the new object at the location of that
# ID in the relation.
at_id = None
try:
at_id = [
t.value
for t in obj.tags
if t.key.startswith(relation_insertion_tag)
][0]
except IndexError:
# did not find insertion tag. at_id is none.
at_id = None
updater.modify_relations_with_object(obj, relation_tag, at_id)
# get modified relations
modified_relations = updater.get_modified_relations()
## Write modified relations too.
if len(modified_relations) > 0:
change_writer.add_modify(modified_relations)

# Write all modified ways with intersections
# Because we have to re-generate nodes for all points
# within the intersecting linestrings, we write
Expand Down Expand Up @@ -796,7 +866,7 @@ def generate_deletions(
osmsrc,
outfile,
compress=True,
skip_nodes=False,
skip_nodes=True,
):
"""
Produce a changefile with <delete> nodes for all IDs in table.
Expand Down
Loading