OpenIndoor architecture

This page explains how the whole architecture of OpenIndoor application.

Data
Data comes from https://download.geofabrik.de/
Example with France / Bretagne: https://download.geofabrik.de/europe/france/bretagne-latest.osm.pbf
First step is to locate building that are indoor “valuable”.
Second step is to extract the whole indoor data we’d like to focus on.

1.1 – Extract indoor data

osmium tags-filter \
–progress \
–output-format=pbf \
–output=/tmp/bretagne_building_indoor.osm.pbf \
./data/bretagne-latest.osm.pbf \
w/indoor w/building:levels

1.2 – Split the area in tiles
To avoid having to manaeg big quantities of data, python recursive splitter() function provides only tiles at zoom where .osm.pbf files are < 100 kB, and remove the bigger or empty ones.

def splitter(
my_uuid="",
input_pbf="data/bretagne-latest.osm.pbf",
zoom=1,
max_zoom = 18,
bbox={"xmin": 0, "ymin": 0, "xmax": 1, "ymax": 1},
name="bretagne"
):
extracts = []
my_finders = []
for x in range(bbox["xmin"], bbox["xmax"] + 1):
for y in range(bbox["ymin"], bbox["ymax"] + 1):
(lon0, lat0) = num2deg(x, y, zoom)
(lon1, lat1) = num2deg(x+1, y+1, zoom)
filename = "/tmp/" \
+ name + "_" + str(zoom) + "_" \
+ str(x) + "_" + str(y) + "_" + str(x + 1) + "_" + str(y + 1) \
+ my_uuid + ".osm.pbf"
# print("setting file: " + filename)
extracts.append({
"output": filename,
"output_format": "pbf",
"bbox": [lon0, lat0, lon1, lat1]
})
my_finders.append({
"zoom": zoom + 1,
"input_pbf": filename,
"bbox": { "xmin": x * 2, "ymin": y * 2, "xmax": (2*x) + 1, "ymax": (2*y) + 1 }
})
conf = '/tmp/config' + my_uuid + '.json'
with open(conf, 'w') as outfile:
json.dump({"extracts": extracts}, outfile)
cmd = "osmium extract " \
+ "--strategy=simple " \
+ "--overwrite " \
+ "--progress " \
+ "--config=" + conf + " " \
+ input_pbf

print("cmd: " + cmd)
indoor_filter = subprocess.run(
cmd,
shell=True
)

for my_finder in my_finders:
if os.path.getsize(my_finder["input_pbf"]) > 100000 and zoom < max_zoom:
splitter(
my_uuid=my_uuid,
input_pbf=my_finder["input_pbf"],
zoom=my_finder["zoom"],
max_zoom=max_zoom,
name=name,
bbox=my_finder["bbox"]
)
os.remove(my_finder["input_pbf"])
elif os.path.getsize(my_finder["input_pbf"]) < 75:
os.remove(my_finder["input_pbf"])

1.3. Extract data

For each tiled .osm.pbf:
1.3.1 – Export data to geojson and import them as geodataframe

with subprocess.Popen(
"""
osmium export \
building_indoor_pbf \
--output-format=geojson
""",
shell=True, stdout=subprocess.PIPE
) as proc_export:
building_indoor_gdf = geopandas.read_file(proc_export.stdout)


1.3.2 – Split buildings and indoor elements

Get building:

# Focus on linestrings (and so polygons)
linestrings = building_indoor_gdf[
building_indoor_gdf['geometry'].apply(
lambda x : x.type=='LineString'
)
]
# Building must the area tagged with 'building:levels'
buildings = linestrings[linestrings['building:levels'].notnull()]
# Convert building linestrings to polygons
buildings.loc[:, 'geometry'] = [
Polygon(mapping(x)['coordinates']) for x in buildings.geometry
]

Get indoor elements:

indoors = building_indoor_gdf[building_indoor_gdf[
'indoor'].notnull()
]

Filter on building that contains indoor elements:

places_gdf = buildings[
buildings.geometry.apply(
lambda x: indoors.intersects(x).any()
)
]

1.3.3 – Prepare geojson multipolygon places that will be used to extract OSM data

# Initialize multi polygon structure
building_indoors = {"type": "FeatureCollection", "features": [{
"type":"Feature","geometry":{
"type":"MultiPolygon", "coordinates":[]
}
}]}
(...)
# For each places:
places_geojson = json.loads(places_gdf.to_json(na='drop'))
for place_feature in places_geojson['features']:
building_indoors['features'][0]['geometry']['coordinates'].append(
place_feature['geometry']['coordinates']
)
# Write the geojson multi polygon to disk:
with open('buildings_indoor.geojson', 'w') as outfile:
json.dump(building_indoors, outfile)
# Extract all data from the very first main .osm.pbf file
cmd = "osmium extract " \
+ "--strategy=simple " \
+ "--overwrite " \
+ "--progress " \
+ "--polygon=" + polygon_file + " " \
+ "--output=" + places_file_pbf + " " \
+ input_pbf
print("cmd: " + cmd)
indoor_filter = subprocess.run(
cmd,
shell=True
)

Leave a Reply

Your email address will not be published. Required fields are marked *