nodes: 0, module; 1, function_definition; 2, function_name:reducer_metro; 3, parameters; 4, block; 5, identifier:self; 6, identifier:metro; 7, identifier:values; 8, expression_statement; 9, expression_statement; 10, for_statement; 11, comment:"""
          Output tags of POI locations nearby tweet locations
  
          Values will be sorted coming into reducer.
          First element in each value tuple will be either 1 (osm POI) or 2 (geotweet).
          Build a spatial index with POI records.
          For each tweet lookup nearby POI, and emit tag values for predefined tags.
          
          """; 12, assignment; 13, pattern_list; 14, call; 15, block; 16, identifier:lookup; 17, call; 18, identifier:i; 19, identifier:value; 20, identifier:enumerate; 21, argument_list; 22, expression_statement; 23, if_statement; 24, identifier:CachedLookup; 25, argument_list; 26, identifier:values; 27, assignment; 28, comparison_operator:type_tag == 1; 29, comment:# OSM POI node, construct geojson and add to Rtree index; 30, block; 31, else_clause; 32, keyword_argument; 33, pattern_list; 34, identifier:value; 35, identifier:type_tag; 36, integer:1; 37, expression_statement; 38, comment:# geotweet, lookup nearest POI from index; 39, block; 40, identifier:precision; 41, identifier:POI_GEOHASH_PRECISION; 42, identifier:type_tag; 43, identifier:lonlat; 44, identifier:data; 45, call; 46, if_statement; 47, expression_statement; 48, expression_statement; 49, comment:# lookup nearby POI from Rtree index (caching results); 50, comment:# for any tags we care about emit the tags value and 1; 51, for_statement; 52, for_statement; 53, attribute; 54, argument_list; 55, not_operator; 56, block; 57, assignment; 58, assignment; 59, identifier:poi; 60, call; 61, block; 62, identifier:poi; 63, call; 64, block; 65, identifier:lookup; 66, identifier:insert; 67, identifier:i; 68, call; 69, attribute; 70, return_statement; 71, identifier:poi_names; 72, list; 73, identifier:kwargs; 74, call; 75, attribute; 76, argument_list; 77, expression_statement; 78, if_statement; 79, identifier:set; 80, argument_list; 81, expression_statement; 82, identifier:dict; 83, argument_list; 84, identifier:lookup; 85, identifier:data_store; 86, identifier:dict; 87, argument_list; 88, identifier:lookup; 89, identifier:get; 90, identifier:lonlat; 91, dictionary_splat; 92, assignment; 93, boolean_operator; 94, block; 95, identifier:poi_names; 96, yield; 97, keyword_argument; 98, keyword_argument; 99, keyword_argument; 100, keyword_argument; 101, identifier:kwargs; 102, identifier:has_tag; 103, list_comprehension; 104, call; 105, comparison_operator:'name' in poi['tags']; 106, expression_statement; 107, expression_list; 108, identifier:geometry; 109, call; 110, identifier:properties; 111, call; 112, identifier:buffer_size; 113, identifier:POI_DISTANCE; 114, identifier:multiple; 115, True; 116, comparison_operator:tag in poi['tags']; 117, for_in_clause; 118, identifier:any; 119, argument_list; 120, string; 121, subscript; 122, call; 123, tuple; 124, integer:1; 125, identifier:dict; 126, argument_list; 127, identifier:dict; 128, argument_list; 129, identifier:tag; 130, subscript; 131, identifier:tag; 132, identifier:POI_TAGS; 133, identifier:has_tag; 134, string_content:name; 135, identifier:poi; 136, string; 137, attribute; 138, argument_list; 139, identifier:metro; 140, identifier:poi; 141, keyword_argument; 142, keyword_argument; 143, keyword_argument; 144, identifier:poi; 145, string; 146, string_content:tags; 147, identifier:poi_names; 148, identifier:append; 149, subscript; 150, identifier:type; 151, string; 152, identifier:coordinates; 153, call; 154, identifier:tags; 155, identifier:data; 156, string_content:tags; 157, subscript; 158, string; 159, string_content:Point; 160, identifier:project; 161, argument_list; 162, identifier:poi; 163, string; 164, string_content:name; 165, identifier:lonlat; 166, string_content:tags
code: def reducer_metro(self, metro, values):
          """
          Output tags of POI locations nearby tweet locations
  
          Values will be sorted coming into reducer.
          First element in each value tuple will be either 1 (osm POI) or 2 (geotweet).
          Build a spatial index with POI records.
          For each tweet lookup nearby POI, and emit tag values for predefined tags.
          
          """
          lookup = CachedLookup(precision=POI_GEOHASH_PRECISION)
          for i, value in enumerate(values):
              type_tag, lonlat, data = value
              if type_tag == 1:
                  # OSM POI node, construct geojson and add to Rtree index
                  lookup.insert(i, dict(
                      geometry=dict(type='Point', coordinates=project(lonlat)),
                      properties=dict(tags=data)
                  ))
              else:
                  # geotweet, lookup nearest POI from index
                  if not lookup.data_store:
                      return
                  poi_names = []
                  kwargs = dict(buffer_size=POI_DISTANCE, multiple=True)
                  # lookup nearby POI from Rtree index (caching results)
                  # for any tags we care about emit the tags value and 1
                  for poi in lookup.get(lonlat, **kwargs):
                      has_tag = [ tag in poi['tags'] for tag in POI_TAGS ]
                      if any(has_tag) and 'name' in poi['tags']:
                          poi_names.append(poi['tags']['name'])
                  for poi in set(poi_names):
                      yield (metro, poi), 1