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 the required SHAPE_TYPE, i.e. FEATURE and LAYER to create FC map #89

Closed
wants to merge 6 commits into from
Closed
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
66 changes: 50 additions & 16 deletions mapmaker/routing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def pairwise(iterable):
from mapmaker.knowledgebase import AnatomicalNode
from mapmaker.knowledgebase.celldl import FC_CLASS, FC_KIND
from mapmaker.knowledgebase.sckan import PATH_TYPE
from mapmaker.settings import settings
from mapmaker.settings import settings, MAP_KIND
from mapmaker.utils import log
import mapmaker.utils.graph as graph_utils

Expand Down Expand Up @@ -1084,6 +1084,7 @@ def get_centreline_from_containing_features(start_dict, end_dict, feature_ids: s
route_graph.add_edge(*edge, **edge_dict)
path_node_ids.update(node.feature_id for node in edge_dict['network-nodes'])

# get FTU connector if possible
def get_ftu_node(feature):
# looking for FTU if possible
if feature.properties.get('fc-class') != FC_CLASS.FTU:
Expand All @@ -1104,24 +1105,53 @@ def get_ftu_node(feature):
return child_feature
return feature

# get closest representative of feature in FTU since the same node may appear in multiple system
def get_ftu_closest_features():
# cluster nodes on the left and right position
left_dists, right_dists = {}, {}
left_medoids, right_medoids = {}, {}
for node_dict in connectivity_graph.nodes.values():
min_left, max_right = float('inf'), -float('inf')
for f in node_dict['features']:
if f.geometry.centroid.x < min_left:
min_left = f.geometry.centroid.x
left_dists[node_dict['node']] = f.geometry.centroid.x
left_medoids[node_dict['node']] = f
if f.geometry.centroid.x > max_right:
max_right = f.geometry.centroid.x
right_dists[node_dict['node']] = f.geometry.centroid.x
right_medoids[node_dict['node']] = f
# select with smaller medoid
if sum([v-min(left_dists.values()) for v in left_dists.values()]) < sum([v-min(right_dists.values()) for v in right_dists.values()]):
medoids = left_medoids
else:
medoids = right_medoids
return medoids

if self.__flatmap.map_kind == MAP_KIND.FUNCTIONAL:
ftu_features = get_ftu_closest_features()
else:
ftu_features = {}

# select the closest feature of a node with multiple features to it's neighbors
def get_node_feature(node_dict, neighbours) -> Feature:
def get_node_feature(node_dict, neighbours, prev_features) -> Feature:
(features:=list(f for f in node_dict['features'])).sort(key=lambda f: f.id)
selected_feature = features[0]
# in a case of a terminal node having multiple features, select the closest one to it's neighbours
if len(node_dict['features']) > 1:
if len(features) > 1:
log.error(f'{path.id}: Terminal node {node_dict["name"]} has multiple features {sorted(set(f.id for f in node_dict["features"]))}')
feature_distances = {}
neighbour_features = [f for n in neighbours if 'features' in connectivity_graph.nodes[n] for f in connectivity_graph.nodes[n]['features']]
if len(neighbour_features) > 0:
for f in (features:=[f for f in node_dict['features']]):
distances = []
for nf in neighbour_features:
distances += [nf.geometry.centroid.distance(f.geometry.centroid)]
feature_distances[f] = sum(distances)/len(distances)
selected_feature = min(feature_distances, key=feature_distances.get) # type: ignore
if settings.get('NPO', False):
return get_ftu_node(selected_feature)
if self.__flatmap.map_kind != MAP_KIND.FUNCTIONAL:
feature_distances = {}
neighbour_features = [f for n in neighbours if 'features' in connectivity_graph.nodes[n] for f in connectivity_graph.nodes[n]['features']]
if len(neighbour_features) > 0:
for f in features:
distances = []
for nf in neighbour_features:
distances += [nf.geometry.centroid.distance(f.geometry.centroid)]
feature_distances[f] = sum(distances)/len(distances)
selected_feature = min(feature_distances, key=feature_distances.get) # type: ignore
else:
return get_ftu_node(ftu_features[node_dict['node']])
return selected_feature

# handling connectivity with no centreline and no terminal
Expand All @@ -1139,6 +1169,8 @@ def get_node_feature(node_dict, neighbours) -> Feature:

terminal_graphs: dict[tuple, nx.Graph] = {}
visited = set()
previous_features = []

for node, node_dict in connectivity_graph.nodes(data=True):
if node not in visited and (connectivity_graph.degree(node) == 1 or node in pseudo_terminals):
if node_dict['type'] == 'feature':
Expand Down Expand Up @@ -1177,7 +1209,8 @@ def add_paths_to_neighbours(node, node_dict):
for neighbour in (neighbours - visited):
neighbour_dict = connectivity_graph.nodes[neighbour]
degree = connectivity_graph.degree(neighbour)
node_feature = get_node_feature(node_dict, [neighbour])
node_feature = get_node_feature(node_dict, [neighbour], previous_features)
previous_features.append(node_feature)
node_feature_centre = node_feature.geometry.centroid
if (debug_properties:=connectivity_graph.get_edge_data(node, neighbour)) is None:
debug_properties = {}
Expand All @@ -1195,7 +1228,8 @@ def add_paths_to_neighbours(node, node_dict):
terminal_graph.nodes[closest_feature_id]['upstream'] = True
terminal_graph.nodes[closest_feature_id]['segments'] = segments
else:
neighbour_feature = get_node_feature(neighbour_dict, [node])
neighbour_feature = get_node_feature(neighbour_dict, [node], previous_features)
previous_features.append(neighbour_feature)
terminal_graph.add_node(neighbour_feature.id, feature=neighbour_feature)
terminal_graph.add_edge(node_feature.id, neighbour_feature.id, **debug_properties)
elif neighbour_dict['type'] == 'segment':
Expand Down
2 changes: 2 additions & 0 deletions mapmaker/shapes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class SHAPE_TYPE(str, Enum): ## Or IntEnum ??
IMAGE = 'image'
TEXT = 'text'
UNKNOWN = 'unknown'
FEATURE = 'feature'
LAYER = 'layer'

#===============================================================================

Expand Down
1 change: 1 addition & 0 deletions mapmaker/sources/powerpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

def set_relationship_property(feature, property, relatives):
geojson_ids = set(s.global_shape.geojson_id for s in relatives if s.global_shape.geojson_id)
geojson_ids = set(s.properties.get('geojson_id') for s in relatives if s.properties.get('geojson_id') is not None)
if feature.has_property(property):
feature.get_property(property).update(geojson_ids)
else:
Expand Down
Loading