|
1 #!/usr/bin/env python3 |
|
2 import sys, json |
|
3 from misc import * |
|
4 from geometry import * |
|
5 from configparser import ConfigParser |
|
6 |
|
7 class Blockmap: |
|
8 ''' |
|
9 The blockmap is a grid of latitude and longitude lines and models |
|
10 a block -> set relation. A block is a latitude and longitude square. |
|
11 ''' |
|
12 block_size = 200.0 # How big are blocks? |
|
13 def __init__(self, blocks = None): |
|
14 from collections import defaultdict |
|
15 self.blocks = blocks or defaultdict(set) |
|
16 def __getitem__(self, blockid): |
|
17 ''' |
|
18 Returns a block for block coordinates. The block is a set that can |
|
19 contain anything. |
|
20 ''' |
|
21 return self.blocks[blockid] |
|
22 def blockpoint(self, point): |
|
23 ''' |
|
24 Returns blockmap coordinates for geographical coordinates. |
|
25 The blockmap coordinates refer to a block in the blockmap. |
|
26 ''' |
|
27 block_coordinate = lambda x: int(x * self.block_size) |
|
28 return block_coordinate(point.x), block_coordinate(point.y) |
|
29 |
|
30 def blocks_in_shape(blockmap, shape): |
|
31 ''' |
|
32 Finds all blocks inside the bounding box of a shape. |
|
33 ''' |
|
34 from itertools import product |
|
35 min_x, max_x = minmax(point.x for point in shape.points) |
|
36 min_y, max_y = minmax(point.y for point in shape.points) |
|
37 min_blockpoint = blockmap.blockpoint(Location(min_x, min_y)) |
|
38 max_blockpoint = blockmap.blockpoint(Location(max_x, max_y)) |
|
39 range_x = range(min_blockpoint[0], max_blockpoint[0] + 1) |
|
40 range_y = range(min_blockpoint[1], max_blockpoint[1] + 1) |
|
41 yield from (blockmap[x, y] for x, y in product(range_x, range_y)) |
|
42 |
|
43 def create_blockmap(regions): |
|
44 ''' |
|
45 Creates a blockmap of regions |
|
46 ''' |
|
47 blockmap = Blockmap() |
|
48 for region in regions.values(): |
|
49 # Minor shapes contain major shapes, so just use those |
|
50 for shape in (region['minor_shapes'] or region['major_shapes']): |
|
51 for block in blocks_in_shape(blockmap, shape): |
|
52 set.add(block, region['name']) |
|
53 return blockmap |
|
54 |
|
55 def get_args(): |
|
56 from argparse import ArgumentParser |
|
57 parser = ArgumentParser() |
|
58 parser.add_argument('gtfs_zip') |
|
59 parser.add_argument('profile') |
|
60 return parser.parse_args() |
|
61 |
|
62 def test_shapes(shapes, point): |
|
63 return any(shape.contains_point(point) for shape in shapes) |
|
64 |
|
65 class RegionalLocation: |
|
66 def __init__(self, *, region, region_class): |
|
67 self.region, self.region_class = region, region_class |
|
68 def __repr__(self): |
|
69 return str.format( |
|
70 'RegionalLocation(region = {region}, region_class = {region_class})', |
|
71 region = repr(self.region), |
|
72 region_class = repr(self.region_class), |
|
73 ) |
|
74 |
|
75 def locate_regionally(position, region): |
|
76 if test_shapes(region['major_shapes'], position): |
|
77 return RegionalLocation(region = region['name'], region_class = 'major') |
|
78 elif test_shapes(region['minor_shapes'], position): |
|
79 return RegionalLocation(region = region['name'], region_class = 'minor') |
|
80 else: |
|
81 return None |
|
82 |
|
83 def find_region_for_point(position, regions, blockmap): |
|
84 for region_name in blockmap[blockmap.blockpoint(position)]: |
|
85 region = regions[region_name] |
|
86 classification = locate_regionally(position, region) |
|
87 if classification: |
|
88 return classification |
|
89 |
|
90 class RegionTester: |
|
91 def __init__(self, regions): |
|
92 self.regions = regions |
|
93 self.blockmap = create_blockmap(regions) |
|
94 def __call__(self, latitude, longitude): |
|
95 return find_region_for_point( |
|
96 position = Location(float(latitude), float(longitude)), |
|
97 regions = self.regions, |
|
98 blockmap = self.blockmap, |
|
99 ) |