Skip to content

Commit

Permalink
Merge branch 'aois' into 'main'
Browse files Browse the repository at this point in the history
Incremental AOI updates for map building

See merge request sim/mosstool!10
  • Loading branch information
chenchenplus committed Jul 2, 2024
2 parents 639c7f9 + 7e8abbe commit b5e8344
Show file tree
Hide file tree
Showing 13 changed files with 537 additions and 449 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
ignore/
cache/
*.html
*.json
*.pkl
*.ipynb
# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
13 changes: 2 additions & 11 deletions mosstool/map/_map_util/add_aoi_pop.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,12 @@
import rasterio
from shapely.geometry import MultiPolygon, Point, Polygon

from .._map_util.aoiutils import geo_coords
from .const import *

__all__ = ["add_aoi_pop"]


def _coords(geo):
if isinstance(geo, Polygon):
return list(geo.exterior.coords)
elif isinstance(geo, MultiPolygon):
all_coords = []
for p_geo in geo.geoms:
all_coords.extend(list(p_geo.exterior.coords))
return all_coords
else:
return list(geo.coords)


def _gps_distance(
Expand Down Expand Up @@ -211,7 +202,7 @@ def _get_aoi_poly_pop_unit(aoi):
return aoi, HAS_INSIDE_PIXEL

# If aoi is too small so that no pixel falls within it, take the population of the pixel where it is located.
x, y = _coords(poly.centroid)[0][:2]
x, y = geo_coords(poly.centroid)[0][:2]
try:
t = (
pixel_idx2point_pop[
Expand Down
37 changes: 24 additions & 13 deletions mosstool/map/_map_util/aoi_matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .._util.angle import abs_delta_angle, delta_angle
from .._util.line import (connect_line_string, get_line_angle,
get_start_vector, line_extend, offset_lane)
from .aoiutils import geo_coords

# ATTENTION: In order to achieve longer distance POI merging, the maximum recursion depth needs to be modified.
sys.setrecursionlimit(50000)
Expand Down Expand Up @@ -47,7 +48,7 @@ def _split_merged_poi_unit(points):
else:
# Post-processing uses road network segmentation to form polygons
lines = [] # dividing lines
(x, y) = convex.centroid.coords[:][0][:2]
(x, y) = geo_coords(convex.centroid)[0][:2]
length = convex.length
# {'id', 'geo', 'point', 'length'}
for lane in road_lane_matcher:
Expand Down Expand Up @@ -323,7 +324,7 @@ def _process_matched_result(aoi, d_matched, w_matched):
# huge_candidate = []
# (x, y) = geo.centroid.coords[:][0][:2]
# length = geo.length
# bound_poss = [c[:2] for c in geo.exterior.coords[:-1]]
# bound_poss = [c[:2] for c in geo_coords(geo)[:-1]]
# for lane in matcher: # {'id', 'geo', 'point', 'length'}
# mid_x, mid_y = lane["point"][:2]
# dis_upper_bound = (
Expand Down Expand Up @@ -464,19 +465,29 @@ def _str_tree_matcher_unit(
matcher_lane_tree,
dis_gate,
huge_gate,
direction_geos: Optional[Dict[int, List[LineString]]] = None,
):
global LENGTH_PER_DOOR, MAX_DOOR_NUM, AOI_GATE_OFFSET
matched = []
bound_poss = [c[:2] for c in geo.exterior.coords[:-1]]
bound_poss = [c[:2] for c in geo_coords(geo)[:-1]]
small_tree_ids = matcher_lane_tree.query(geo.buffer(dis_gate))
stop_lane_angles = (
[[get_line_angle(l) for l in lanes] for lanes in direction_geos.values()]
if direction_geos is not None
else None
)
for tid in small_tree_ids:
lane = matcher[tid] # {'id', 'geo', 'point', 'length'}
p_aoi, p_lane = ops.nearest_points(
geo, lane["geo"]
) # Returns the calculated nearest points in the input geometries
distance = p_aoi.distance(p_lane)
# if distance < dis_gate:
if True:
lane_angle = get_line_angle(lane["geo"])
if stop_lane_angles is None or any(
np.mean([abs_delta_angle(lane_angle, angle) for angle in angles])
< np.pi / 4
for angles in stop_lane_angles
):
# Project the point on the lane closest to the poly to the lane and return s
s = lane["geo"].project(p_lane)
if (
Expand Down Expand Up @@ -596,7 +607,7 @@ def _add_point_aoi_unit(arg):
global W_DIS_GATE, W_HUGE_GATE
aoi, aoi_type = arg
geo = aoi["geo"]
x, y = geo.coords[:][0][:2]
x, y = geo_coords(geo)[0][:2]
d_matched, w_matched = [], []
for (
matcher,
Expand Down Expand Up @@ -717,7 +728,7 @@ def _add_poly_aoi_unit(arg):
base_aoi = {
"id": 0, # It is difficult to deal with the problem of uid += 1 during parallelization, so assign the id after parallelization is completed.
"type": aoi_type,
"positions": [{"x": c[0], "y": c[1]} for c in geo.exterior.coords[:]],
"positions": [{"x": c[0], "y": c[1]} for c in geo_coords(geo)],
"area": geo.area,
"external": {
"osm_tencent_ids": [
Expand Down Expand Up @@ -749,7 +760,7 @@ def _add_aoi_stop_unit(arg):
global LENGTH_PER_DOOR, MAX_DOOR_NUM, AOI_GATE_OFFSET
aoi, aoi_type = arg
geo = aoi["geo"]
bound_poss = [c[:2] for c in geo.exterior.coords[:-1]]
bound_poss = [c[:2] for c in geo_coords(geo)[:-1]]
station_type = aoi["external"]["station_type"]
d_matched, w_matched = [], []
if station_type == "SUBWAY":
Expand Down Expand Up @@ -823,7 +834,7 @@ def _add_aoi_stop_unit(arg):
base_aoi = {
"id": 0, # It is difficult to deal with the problem of uid += 1 during parallelization, so assign the id after parallelization is completed.
"type": aoi_type,
"positions": [{"x": c[0], "y": c[1]} for c in geo.exterior.coords[:]],
"positions": [{"x": c[0], "y": c[1]} for c in geo_coords(geo)],
"area": geo.area,
"external": {
"osm_tencent_ids": [
Expand Down Expand Up @@ -925,10 +936,10 @@ def map_land_use(euluc: int) -> int:
polygon = candidate_poly
else:
polygon = MultiPoint(
[pt for g in geo.geoms for pt in g.exterior.coords]
[pt for g in geo.geoms for pt in geo_coords(g)]
).convex_hull
else:
polygon = MultiPoint([pt for pt in polygon.exterior.coords]).convex_hull
polygon = MultiPoint([pt for pt in geo_coords(polygon)]).convex_hull
df.geometry[i] = polygon

# Processing AOIs
Expand Down Expand Up @@ -959,7 +970,7 @@ def map_land_use(euluc: int) -> int:
polygon = candidate_poly
else:
polygon = MultiPoint(
[pt for g in geo.geoms for pt in g.exterior.coords]
[pt for g in geo.geoms for pt in geo_coords(g)]
).convex_hull
else:
aoi["land_use"] = map_land_use(-1) # 默认
Expand Down Expand Up @@ -1630,7 +1641,7 @@ def _merge_covered_aoi(aois, workers):
# Pre-compute geometric properties
for aoi in aois:
geo = aoi["geo"]
aoi["point"] = list(geo.centroid.coords)[0] # Geometric center
aoi["point"] = geo_coords(geo.centroid)[0] # Geometric center
aoi["length"] = geo.length # Perimeter
aoi["area"] = geo.area # area
aoi["valid"] = geo.is_valid
Expand Down
43 changes: 28 additions & 15 deletions mosstool/map/_map_util/aoiutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,27 @@
__all__ = [
"generate_aoi_poi",
"generate_sumo_aoi_poi",
"geo_coords",
]


def _fix_polygon(polygon: Polygon):
if polygon.is_valid:
return polygon
def geo_coords(geo):
if isinstance(geo, Polygon):
return list(geo.exterior.coords)
elif isinstance(geo, MultiPolygon):
all_coords = []
for p_geo in geo.geoms:
all_coords.extend(geo_coords(p_geo))
return all_coords
else:
return list(geo.coords)


def _fix_polygon(input_poly: Polygon):
if input_poly.is_valid:
return input_poly
else:
geo = polygon.buffer(0)
geo = input_poly.buffer(0)
if isinstance(geo, Polygon) and geo.is_valid:
polygon = geo
elif isinstance(geo, MultiPolygon):
Expand All @@ -31,10 +44,10 @@ def _fix_polygon(polygon: Polygon):
polygon = candidate_poly
else:
polygon = MultiPoint(
[pt for g in geo.geoms for pt in g.exterior.coords]
[pt for g in geo.geoms for pt in geo_coords(g)]
).convex_hull
else:
polygon = MultiPoint([pt for pt in polygon.exterior.coords]).convex_hull
polygon = MultiPoint([pt for pt in geo_coords(input_poly)]).convex_hull
return polygon


Expand All @@ -44,7 +57,7 @@ def _fix_aois_poly(input_aois: list) -> list:
coords = aoi["coords"]
geo = _fix_polygon(Polygon(coords))
if geo.is_valid and geo:
aoi["coords"] = [c for c in geo.exterior.coords]
aoi["coords"] = [c for c in geo_coords(geo)]
aois.append(aoi)
return aois

Expand All @@ -54,7 +67,7 @@ def _connect_aoi_unit1(poly):
Find the children aoi of each poly in the merged geometry
"""
global aois_small
x, y = list(poly.centroid.coords)[0]
x, y = geo_coords(poly.centroid)[0]
length = poly.length
children = []
added_aoi_small = []
Expand All @@ -81,7 +94,7 @@ def _connect_aoi_unit2(arg):
] # There is only 1 child, indicating that it is not connected to other aoi and restores the original shape

poly_inner = MultiPoint(
[pt for aoi in children for pt in aoi["geo"].exterior.coords]
[pt for aoi in children for pt in geo_coords(aoi["geo"])]
).convex_hull
poly = poly.intersection(poly_inner)
if not isinstance(poly, Polygon):
Expand Down Expand Up @@ -113,7 +126,7 @@ def _connect_aoi_unit2(arg):
"inner_poi": inner_poi,
"inner_poi_catg": inner_poi_catg,
},
"point": list(poly.centroid.coords)[
"point": geo_coords(poly.centroid)[
0
], # For subsequent processing needs, calculate the merged geometric center
"length": poly.length, # perimeter
Expand Down Expand Up @@ -171,7 +184,7 @@ def _merge_aoi(input_aois: list, merge_aoi: bool = False, workers: int = 32):
logging.warning(f"Invalid polygon {aoi['id']}")
continue
aoi["geo"] = geo
aoi["point"] = list(geo.centroid.coords)[0] # Geometric center
aoi["point"] = geo_coords(geo.centroid)[0] # Geometric center
aoi["length"] = geo.length # Perimeter
aoi["area"] = geo.area # area
aois.append(aoi)
Expand Down Expand Up @@ -221,7 +234,7 @@ def _connect_aoi(input_aois: list, merge_aoi: bool = False, workers: int = 32):
logging.warning(f"Invalid polygon {aoi['id']}")
continue
aoi["geo"] = geo
aoi["point"] = list(geo.centroid.coords)[0] # Geometric center
aoi["point"] = geo_coords(geo.centroid)[0] # Geometric center
aoi["length"] = geo.length # Perimeter
aoi["area"] = geo.area # area
aois.append(aoi)
Expand All @@ -239,7 +252,7 @@ def _connect_aoi(input_aois: list, merge_aoi: bool = False, workers: int = 32):
polys = [aoi["geo"] for aoi in aois_small]
polys_scale = [scale(p, xfact=SCALE, yfact=SCALE, origin="centroid") for p in polys]
geo_scale_connect = ops.unary_union(polys_scale)
args = list(geo_scale_connect.geoms)
args = list(geo_scale_connect.geoms) # type: ignore
with Pool(processes=workers) as pool:
results = pool.map(
_connect_aoi_unit1,
Expand Down Expand Up @@ -326,7 +339,7 @@ def point_extend(center: Point, length: float):
geo = Polygon(coords)
geo = _fix_polygon(geo)
stop["geo"] = geo
stop["point"] = list(geo.centroid.coords)[0] # Geometric center
stop["point"] = geo_coords(geo.centroid)[0] # Geometric center
stop["length"] = geo.length # Perimeter
stop["area"] = geo.area # area
return stops
Expand Down Expand Up @@ -381,7 +394,7 @@ def _match_poi_to_aoi(aois, pois, workers):
def _post_compute_aoi_poi(aois, pois_isolate):
# Update coordinates
for a in aois:
coords = a["geo"].exterior.coords[:]
coords = geo_coords(a["geo"])
a["coords"] = coords
# poi becomes Aoi independently
for p in pois_isolate:
Expand Down
27 changes: 19 additions & 8 deletions mosstool/map/_map_util/map_aois_matchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from ...type import Map
from ...util.format_converter import pb2dict
from .aoi_matcher import _process_matched_result,_str_tree_matcher_unit
from .aoi_matcher import _process_matched_result, _str_tree_matcher_unit
from .const import *


Expand All @@ -23,12 +23,12 @@ def _add_aoi_unit(aoi):
global d_matcher, w_matcher
global D_DIS_GATE, D_HUGE_GATE
global W_DIS_GATE, W_HUGE_GATE
global d_tree,w_tree
global d_tree, w_tree
geo = _map_aoi2geo(aoi)
# d_matched = _matcher_unit(geo, d_matcher, D_DIS_GATE, D_HUGE_GATE)
# w_matched = _matcher_unit(geo, w_matcher, W_DIS_GATE, W_HUGE_GATE)
d_matched = _str_tree_matcher_unit(geo, d_matcher, d_tree,D_DIS_GATE, D_HUGE_GATE)
w_matched = _str_tree_matcher_unit(geo, w_matcher, w_tree,W_DIS_GATE, W_HUGE_GATE)
d_matched = _str_tree_matcher_unit(geo, d_matcher, d_tree, D_DIS_GATE, D_HUGE_GATE)
w_matched = _str_tree_matcher_unit(geo, w_matcher, w_tree, W_DIS_GATE, W_HUGE_GATE)
aoi["external"] = {}
if d_matched or w_matched:
return _process_matched_result(
Expand All @@ -38,7 +38,7 @@ def _add_aoi_unit(aoi):

def match_map_aois(net: Map, matchers: dict, workers: int):
global d_matcher, w_matcher
global d_tree,w_tree
global d_tree, w_tree
net_dict = pb2dict(net)
orig_aois = net_dict["aois"]
orig_pois = net_dict["pois"]
Expand All @@ -48,27 +48,38 @@ def match_map_aois(net: Map, matchers: dict, workers: int):
)
d_tree = STRtree([l["geo"] for l in d_matcher])
w_tree = STRtree([l["geo"] for l in w_matcher])
results_aoi = []
results_aois = []
for i in range(0, len(orig_aois), MAX_BATCH_SIZE):
args_batch = orig_aois[i : i + MAX_BATCH_SIZE]
with Pool(processes=workers) as pool:
results_aoi += pool.map(
results_aois += pool.map(
_add_aoi_unit,
args_batch,
chunksize=min(ceil(len(args_batch) / workers), 500),
)
aois = [r for r in results_aoi if r]
aois = [r for r in results_aois if r]
# filter pois
all_poi_ids = set(pid for a in aois for pid in a["poi_ids"])
pois = [p for p in orig_pois if p["id"] in all_poi_ids]
# rearrange aoi id
aois_dict = {}
_poi_uid_dict = {
poi["id"]: poi_id for poi_id, poi in enumerate(pois, start=POI_START_ID)
}
for aoi_id, aoi in enumerate(aois, start=AOI_START_ID):
aoi["id"] = aoi_id
aois_dict[aoi_id] = aoi
# update poi_ids
aoi["poi_ids"] = [_poi_uid_dict[pid] for pid in aoi["poi_ids"]]
# rearrange poi id
pois_dict = {}
_valid_aois = [(a["id"], aoi) for aoi, a in zip(results_aois, orig_aois) if aoi]
_aoi_uid_dict = {
aid: AOI_START_ID + idx for idx, (aid, _) in enumerate(_valid_aois)
}
for poi_id, poi in enumerate(pois, start=POI_START_ID):
poi["id"] = poi_id
pois_dict[poi_id] = poi
# update aoi_id
poi["aoi_id"] = _aoi_uid_dict[poi["aoi_id"]]
return (aois_dict, pois_dict)
Loading

0 comments on commit b5e8344

Please sign in to comment.