add some initial work on region trimmer
This commit is contained in:
152
contrib/regionTrimmer.py
Normal file
152
contrib/regionTrimmer.py
Normal file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""Deletes outlying unconnected regions"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
|
||||
import networkx
|
||||
|
||||
# incantation to be able to import overviewer_core
|
||||
#if not hasattr(sys, "frozen"):
|
||||
# sys.path.insert(0, os.path.abspath(os.path.join(os.path.split(__file__)[0], '..')))
|
||||
#import overviewer_core
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
def get_region_file_from_node(regionset_path, node):
|
||||
return os.path.join(regionset_path, 'r.%d.%d.mca' % node)
|
||||
|
||||
def get_nodes(regionset_path):
|
||||
return [tuple(map(int, r.split('.')[1:3])) \
|
||||
for r in glob.glob(os.path.join(regionset_path, 'r.*.*.mca'))]
|
||||
|
||||
def generate_edges(graph):
|
||||
offsets = (-1, 1)
|
||||
nodes = graph.nodes()
|
||||
for node in nodes:
|
||||
for offset in offsets:
|
||||
graph.add_edges_from((node, offset_node) for offset_node in \
|
||||
[(node[0] + offset, node[1]), (node[0], node[1] + offset), \
|
||||
(node[0] + offset, node[1] + offset)] \
|
||||
if offset_node in nodes)
|
||||
return graph
|
||||
|
||||
def generate_subgraphs(nodes):
|
||||
graph = networkx.Graph()
|
||||
graph.add_nodes_from(nodes)
|
||||
generate_edges(graph)
|
||||
return graph, networkx.connected_component_subgraphs(graph)
|
||||
|
||||
def get_graph_bounds(graph):
|
||||
nodes = graph.nodes()
|
||||
return (
|
||||
max(n[0] for n in nodes),
|
||||
min(n[0] for n in nodes),
|
||||
max(n[1] for n in nodes),
|
||||
min(n[1] for n in nodes),
|
||||
)
|
||||
|
||||
def get_graph_center_by_bounds(bounds):
|
||||
dx = bounds[0] - bounds[1]
|
||||
dy = bounds[2] - bounds[3]
|
||||
return (dx / 2 + bounds[1], dy / 2 + bounds[3])
|
||||
|
||||
def main(*args, **options):
|
||||
if len(args) < 1:
|
||||
logger.error('Missing region directory argument')
|
||||
return
|
||||
for path in args:
|
||||
logger.info('Using regionset path: %s', path)
|
||||
nodes = get_nodes(path)
|
||||
if not len(nodes):
|
||||
logger.error('Found no nodes, are you sure there are .mca files in %s ?',
|
||||
path)
|
||||
return
|
||||
logger.info('Found %d nodes', len(nodes))
|
||||
logger.info('Generating graphing nodes...')
|
||||
graph, subgraphs = generate_subgraphs(nodes)
|
||||
assert len(graph.nodes()) == sum(len(sg.nodes()) for sg in subgraphs)
|
||||
if len(subgraphs) == 1:
|
||||
logger.warn('All regions are contiguous, no work needs doing!')
|
||||
return
|
||||
logger.info('Found %d discrete region sections', len(subgraphs))
|
||||
subgraphs = sorted(subgraphs, key=lambda sg: len(sg), reverse=True)
|
||||
for i, sg in enumerate(subgraphs):
|
||||
logger.info('Region section #%02d: %04d nodes', i+1, len(sg.nodes()))
|
||||
bounds = get_graph_bounds(sg)
|
||||
logger.info('Bounds: %d <-> %d x %d <-> %d', *get_graph_bounds(sg))
|
||||
center = get_graph_center_by_bounds(bounds)
|
||||
logger.info('Center: %d x %d', *center)
|
||||
|
||||
main_section = subgraphs[0]
|
||||
main_section_bounds = get_graph_bounds(main_section)
|
||||
main_section_center = get_graph_center_by_bounds(main_section_bounds)
|
||||
logger.info('Using %d node graph as main section,', len(main_section.nodes()))
|
||||
satellite_sections = subgraphs[1:]
|
||||
for ss in satellite_sections:
|
||||
bounds = get_graph_bounds(ss)
|
||||
center = get_graph_center_by_bounds(bounds)
|
||||
logger.info('Checking satellite section with %d nodes, %d <-> %d x %d <-> %d bounds and %d x %d center',
|
||||
len(ss.nodes()), *(bounds + center))
|
||||
if options['trim_disconnected']:
|
||||
logger.info('Trimming regions: %s', ', '.join(
|
||||
get_region_file_from_node(path, n) for n in ss.nodes()))
|
||||
for region_file in (get_region_file_from_node(path, n) \
|
||||
for n in ss.get_nodes()):
|
||||
if not options['dry_run']:
|
||||
os.unlink(region_file)
|
||||
if options['trim_outside_main']:
|
||||
if center[0] <= main_section_bounds[0] and center[0] >= main_section_bounds[1] and \
|
||||
center[1] <= main_section_bounds[2] and center[1] >= main_section_bounds[3]:
|
||||
logger.info('Section falls inside main section bounds, ignoring')
|
||||
else:
|
||||
logger.info('Section is outside main section bounds')
|
||||
logger.info('Trimming regions: %s', ', '.join(
|
||||
get_region_file_from_node(path, n) for n in ss.nodes()))
|
||||
for region_file in (get_region_file_from_node(path, n) \
|
||||
for n in ss.nodes()):
|
||||
if not options['dry_run']:
|
||||
os.unlink(region_file)
|
||||
if options['trim_outside_bounds']:
|
||||
x = map(int, options['trim_outside_bounds'].split(','))
|
||||
if len(x) == 4:
|
||||
trim_center = x[:2]
|
||||
trim_bounds = x[2:]
|
||||
elif len(x) == 2:
|
||||
trim_center = main_section_center
|
||||
trim_bounds = x
|
||||
else:
|
||||
logger.error('Invalid center/bound value: %s',
|
||||
options['trim_outside_bounds'])
|
||||
continue
|
||||
for node in ss.nodes():
|
||||
if node[0] >= trim_center[0] + trim_bounds[0] or \
|
||||
node[0] <= trim_center[0] - trim_bounds[0] or \
|
||||
node[1] >= trim_center[1] + trim_bounds[1] or \
|
||||
node[1] <= trim_center[1] - trim_bounds[1]:
|
||||
region_file = get_region_file_from_node(path, node)
|
||||
logger.info('Region falls outside specified bounds, trimming: %s',
|
||||
region_file)
|
||||
if not options['dry_run']:
|
||||
os.unlink(region_file)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import optparse
|
||||
logging.basicConfig()
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-D', '--trim-disconnected', action='store_true', default=False,
|
||||
help='Trim all disconnected regions')
|
||||
parser.add_option('-M', '--trim-outside-main', action='store_true', default=False,
|
||||
help='Trim disconnected regions outside main section bounds')
|
||||
parser.add_option('-B', '--trim-outside-bounds', default=False,
|
||||
metavar='[center_X,center_Y,]bound_X,bound_Y',
|
||||
help='Trim outside given bounds (given as [center_X,center_Y,]bound_X,bound_Y)')
|
||||
parser.add_option('-n', '--dry-run', action='store_true', default=False,
|
||||
help='Don\'t actually delete anything')
|
||||
#parser.add_option('-b', )
|
||||
opts, args = parser.parse_args()
|
||||
main(*args, **vars(opts))
|
||||
Reference in New Issue
Block a user