Skip to content

Commit 7c8f234

Browse files
committed
adds lazy mode
1 parent 3a106ff commit 7c8f234

File tree

5 files changed

+94
-14
lines changed

5 files changed

+94
-14
lines changed

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ pytest
5555

5656
## Quick Start
5757

58-
Extract `home`, `work`, `education`, `shop` and various other activity locations ("facilities") for the Isle of Man, using the following command (the path is given from OSMOX project root):
58+
Extract `home`, `work`, `education`, `shop` and various other activity locations ("facilities") for the Isle of Man, using the following steps (paths is given from OSMOX project root):
59+
60+
First download `isle-of-man-latest.osm.pbf` from [geobabrik](https://download.geofabrik.de/europe/isle-of-man.html) and place in an `example` directory.
5961

6062
```{sh}
61-
osmox run configs/example.json example_data/isle-of-man.osm example -crs "epsg:27700"
63+
osmox run configs/example.json example/isle-of-man.osm example -crs "epsg:27700"
6264
```
6365

6466
After about 30 seconds, you should find the outputs in geojson format in the specified `example` directory. The geojson file contains locations for the extracted facilities, and each facility includes a number of features with coordinates given in WGS-84 (EPSG:4326) coordinate reference system (CRS), so that they can be quickly inspected via [kepler](https://kepler.gl) or equivalent.
@@ -91,8 +93,6 @@ After about 30 seconds, you should find the outputs in geojson format in the spe
9193
...
9294
```
9395

94-
95-
9696
![isle of man floor areas](./readme_fixtures/floor-areas.png)
9797
*^ Isle of Man facility `floor_area` feature. Approximated based on polygon areas and floor labels or sensible defaults.*
9898

@@ -106,9 +106,12 @@ After about 30 seconds, you should find the outputs in geojson format in the spe
106106
```{sh}
107107
osmox run --help
108108
109+
Usage: osmox run [OPTIONS] CONFIG_PATH INPUT_PATH OUTPUT_PATH
110+
109111
Options:
110112
-crs, --crs TEXT crs string eg (default): 'epsg:27700' (UK grid)
111113
-s, --single_use split multi-activity facilities into multiple single-activity facilities
114+
-l, --lazy if filtered object already has a label, do not search for more (supresses multi-use but is faster)
112115
--help Show this message and exit.
113116
```
114117

osmox/build.py

+66-4
Original file line numberDiff line numberDiff line change
@@ -117,22 +117,29 @@ def __str__(self):
117117
"""
118118

119119
def add_tags(self, osm_objects):
120+
print(f"adding tag to {self.idx}")
121+
print(f"existing = {self.activity_tags}")
120122
for o in osm_objects:
121123
if o.activity_tags:
122124
self.activity_tags.extend(o.activity_tags)
125+
print(f"updated = {self.activity_tags}")
123126

124127
def apply_default_tag(self, tag):
125128
self.activity_tags = [OSMTag(tag[0], tag[1])]
126129

127130
def assign_points(self, points):
131+
print("assigning points")
128132
snaps = [c for c in points.intersection(self.geom.bounds)]
133+
print(f"snaps = {snaps}")
129134
if snaps:
130135
self.add_tags(snaps)
131136
return True
132137

133138
def assign_areas(self, areas):
139+
print("assigning areas")
134140
snaps = [c for c in areas.intersection(self.geom.bounds)]
135141
snaps = [c for c in snaps if c.geom.contains(self.geom.centroid)]
142+
print(f"snaps = {snaps}")
136143
if snaps:
137144
self.add_tags(snaps)
138145
return True
@@ -192,12 +199,20 @@ class ObjectHandler(osmium.SimpleHandler):
192199
wkbfab = osmium.geom.WKBFactory()
193200
logger = logging.getLogger(__name__)
194201

195-
def __init__(self, config, crs='epsg:27700', from_crs='epsg:4326', level=logging.DEBUG):
202+
def __init__(
203+
self,
204+
config,
205+
crs='epsg:27700',
206+
from_crs='epsg:4326',
207+
lazy=False,
208+
level=logging.DEBUG
209+
):
196210

197211
super().__init__()
198212
logging.basicConfig(level=level)
199213
self.cnfg = config
200214
self.crs = crs
215+
self.lazy = lazy
201216
self.filter = self.cnfg["filter"]
202217
self.object_features = self.cnfg["object_features"]
203218
self.default_tags = self.cnfg["default_tags"]
@@ -285,11 +300,50 @@ def area(self, a):
285300
elif activity_tags:
286301
self.add_area(idx=a.id, activity_tags=activity_tags, geom=self.fab_area(a))
287302

288-
"""
289-
Assign unknown tags to buildings spatially.
290-
"""
291303

292304
def assign_tags(self):
305+
"""
306+
Assign unknown tags to buildings spatially.
307+
"""
308+
if not self.lazy:
309+
self.assign_tags_full()
310+
else:
311+
self.assign_tags_lazy()
312+
313+
def assign_tags_full(self):
314+
"""
315+
Assign unknown tags to buildings spatially.
316+
"""
317+
318+
for obj in helpers.progressBar(self.objects, prefix='Progress:', suffix='Complete', length=50):
319+
print(obj.idx, obj.activity_tags)
320+
321+
if obj.activity_tags:
322+
print("existing:", obj.idx, obj.activity_tags)
323+
# if an onject already has activity tags, continue
324+
self.log["existing"] += 1
325+
326+
if obj.assign_points(self.points):
327+
print("pre point assignment:", obj.idx, obj.activity_tags)
328+
# else try to assign activity tags based on contained point objects
329+
self.log["points"] += 1
330+
continue
331+
332+
if obj.assign_areas(self.areas):
333+
print("pre area assignment:", obj.idx, obj.activity_tags)
334+
# else try to assign activity tags based on containing area objects
335+
self.log["areas"] += 1
336+
continue
337+
338+
if self.default_tags and not obj.activity_tags:
339+
print("defaults need to be assigned...", obj.idx, obj.activity_tags)
340+
# otherwise apply defaults if set
341+
self.log["defaults"] += 1
342+
for a in self.default_tags:
343+
obj.apply_default_tag(a)
344+
345+
def assign_tags_lazy(self):
346+
"""Assign tags if filtered object does not already have useful tags."""
293347

294348
for obj in helpers.progressBar(self.objects, prefix='Progress:', suffix='Complete', length=50):
295349

@@ -401,11 +455,19 @@ def extract_targets(self, target_act):
401455
return MultiPoint(targets)
402456

403457
def geodataframe(self, single_use=False):
458+
print("+++++++++++++++++++")
459+
# print(self.objects)
460+
print(single_use)
461+
404462
if single_use:
463+
print("SINGLE USE")
405464

406465
df = pd.DataFrame(
407466
(summary for o in self.objects for summary in o.single_activity_summaries())
408467
)
468+
print("====")
469+
print(df)
470+
print("====")
409471
return gp.GeoDataFrame(df, geometry='geometry', crs=self.crs)
410472

411473
df = pd.DataFrame(

osmox/main.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from linecache import lazycache
12
import logging
23
import os
34

@@ -45,17 +46,31 @@ def validate(config_path):
4546
help="crs string eg (default): 'epsg:27700' (UK grid)")
4647
@click.option("-s", "--single_use", is_flag=True,
4748
help="split multi-activity facilities into multiple single-activity facilities")
48-
def run(config_path, input_path, output_path, crs, single_use):
49+
@click.option("-l", "--lazy", is_flag=True,
50+
help="if filtered object already has a label, do not search for more (supresses multi-use)")
51+
def run(config_path, input_path, output_path, crs, single_use, lazy):
4952
logger.info(f" Loading config from {config_path}")
5053
cnfg = config.load(config_path)
5154
config.validate_activity_config(cnfg)
5255

5356
if not os.path.exists(output_path):
5457
logger.info(f'Creating output directory: {output_path}')
5558
os.mkdir(output_path)
56-
57-
handler = build.ObjectHandler(cnfg, crs)
58-
logger.info(f" Filtering all objects found in {input_path}.")
59+
60+
logger.info(f"Creating handler with crs: {crs}.")
61+
print(crs)
62+
print(lazy)
63+
if single_use:
64+
logger.info(f"Handler is single-use, different activities will get unique locations. even if these are duplicated.")
65+
if lazy:
66+
logger.info(f"Handler will be using lazy assignment, this may suppress some multi-use.")
67+
68+
handler = build.ObjectHandler(
69+
config=cnfg,
70+
crs=crs,
71+
lazy=lazy
72+
)
73+
logger.info(f" Filtering all objects found in {input_path}. This may take a long while.")
5974
handler.apply_file(str(input_path), locations=True, idx='flex_mem')
6075
logger.info(f" Found {len(handler.objects)} buildings.")
6176
logger.info(f" Found {len(handler.points)} nodes with valid tags.")

tests/fixtures/test_config_leisure.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"leisure": ["pitch", "park", "playground"]
44
},
55

6-
"object_features": ["units", "transit_distance", "levels", "area", "floor_area"],
6+
"object_features": ["units", "levels", "area", "floor_area"],
77

88
"default_tags": [["building", "residential"]],
99

tests/test_activityhandler.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def test_leisure_config():
126126

127127
@pytest.fixture()
128128
def leisureHandler(test_leisure_config):
129-
return build.ObjectHandler(test_leisure_config, crs='epsg:4326')
129+
return build.ObjectHandler(test_leisure_config, crs='epsg:4326', lazy=False)
130130

131131

132132
def test_leisure_handler(leisureHandler):

0 commit comments

Comments
 (0)