Created
December 13, 2025 13:39
-
-
Save jwheare/a3577de092c68b1789e454e832b38f5b to your computer and use it in GitHub Desktop.
OSM slipway analysis - takes overpass geojson as input
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import json | |
| import sys | |
| from colors import color # pip install ansicolors | |
| def main(input_path): | |
| with open(input_path, "r") as f: | |
| geojson = json.load(f) | |
| features = geojson['features'] | |
| f_dict = { | |
| 'node': {}, | |
| 'way': {}, | |
| } | |
| for f in features: | |
| props = f['properties'] | |
| f_type = props['type'] | |
| f_id = props['id'] | |
| if f_type in f_dict: | |
| f_dict[f_type][f_id] = f | |
| else: | |
| print(f'[{f_id}] unknown type: {f_type}') | |
| orphan_nodes = [] | |
| for n_id, n_feat in f_dict['node'].items(): | |
| n_ways = [] | |
| for w_id, w_feat in f_dict['way'].items(): | |
| if n_id in w_feat['properties']['nodes']: | |
| n_ways.append(w_id) | |
| if not len(n_ways): | |
| orphan_nodes.append(n_id) | |
| print(f'[{n_id}] orphan node') | |
| n_feat['properties']['ways'] = n_ways | |
| missing_slip_node = [] | |
| slip_node_starts = [] | |
| slip_node_middles = [] | |
| slip_node_ends = [] | |
| way_tags = [ | |
| ('leisure', 'slipway', "magenta"), | |
| ('service', 'slipway', "purple"), | |
| ('highway', 'service', "blue"), | |
| ] | |
| way_tag_counts = {} | |
| way_full_tag_counts = {} | |
| for w_id, w_feat in f_dict['way'].items(): | |
| has_slip_node = False | |
| slip_node_at_end = False | |
| slip_node_at_start = False | |
| slip_node_in_middle = False | |
| w_nodes = w_feat['properties']['nodes'] | |
| for n_id in w_nodes: | |
| if n_id in f_dict['node']: | |
| has_slip_node = True | |
| slip_node_index = w_nodes.index(n_id) | |
| if slip_node_index == 0: | |
| slip_node_at_start = True | |
| elif slip_node_index == len(w_nodes) - 1: | |
| slip_node_at_end = True | |
| else: | |
| slip_node_in_middle = True | |
| tag_parts = [] | |
| w_tags = w_feat['properties']['tags'] | |
| key_parts = [] | |
| for t, v, COLOR in way_tags: | |
| if t in w_tags: | |
| k = f'{t}={w_tags[t]}' | |
| key_parts.append(k) | |
| if k not in way_tag_counts: | |
| way_tag_counts[k] = 0 | |
| way_tag_counts[k] += 1 | |
| V_COL = "gray" | |
| if w_tags[t] != v: | |
| V_COL = "orange" | |
| tag_parts.append(f' {color(t, COLOR)} = {color(w_tags[t], V_COL)}') | |
| slip_node_status = color('none', 'grey') | |
| if has_slip_node: | |
| pos_parts = [] | |
| if slip_node_at_end: | |
| slip_node_ends.append(w_id) | |
| pos_parts.append(color('end', 'green')) | |
| if slip_node_at_start: | |
| print(f'[{w_id}] way has slip node at start ({len(w_feat['properties']['nodes'])})') | |
| pos_parts.append(color('start', 'yellow')) | |
| slip_node_starts.append(w_id) | |
| if slip_node_in_middle: | |
| print(f'[{w_id}] way has slip node in middle ({len(w_feat['properties']['nodes'])})') | |
| slip_node_middles.append(w_id) | |
| pos_parts.append(color('middle', 'red')) | |
| slip_node_status = ",".join(pos_parts) | |
| else: | |
| missing_slip_node.append(w_id) | |
| print(f'[{w_id}] way missing leisure=slip node ({len(w_feat['properties']['nodes'])})') | |
| full_key = (';'.join(key_parts), slip_node_status) | |
| if full_key not in way_full_tag_counts: | |
| way_full_tag_counts[full_key] = 0 | |
| way_full_tag_counts[full_key] += 1 | |
| if not has_slip_node or slip_node_at_start or slip_node_in_middle: | |
| for p in tag_parts: | |
| print(p) | |
| print(f'nodes: {len(f_dict['node'])}') | |
| print(f' orphans: {len(orphan_nodes)}') | |
| print(f'ways: {len(f_dict['way'])}') | |
| print(f' leisure=slip node missing: {len(missing_slip_node)}') | |
| print(f' leisure=slip node at start: {len(slip_node_starts)}') | |
| print(f' leisure=slip node in middle: {len(slip_node_middles)}') | |
| print(f' leisure=slip node at end: {len(slip_node_ends)}') | |
| print('way tags') | |
| for k, t_count in sorted(way_tag_counts.items(), key=lambda x: x[1], reverse=True): | |
| print(f' {k} ({t_count})') | |
| print('way tags (full)') | |
| for (k, slip_node), t_count in sorted(way_full_tag_counts.items(), key=lambda x: (x[0][1], x[1]), reverse=True): | |
| print(f' slip_node={slip_node} {k} ({t_count})') | |
| if __name__ == "__main__": | |
| if len(sys.argv) < 2: | |
| print("Usage: python slipways.py input.json") | |
| sys.exit(1) | |
| input_path = sys.argv[1] | |
| main(input_path) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment