#!/usr/bin/env python3 """Audit whether IMG-derived POIs have both OSM display tags and names. This measures converted OSM, NOT actual OsmAnd screen visibility nor .obf indexing. Output inventory always includes Garmin codes and example coordinates. """ import argparse,csv,gzip,json from pathlib import Path from collections import Counter,defaultdict from xml.etree.ElementTree import iterparse CLASS_KEYS=('amenity','tourism','historic','shop','emergency','leisure','natural','man_made','place','highway','landuse','power','waterway','aerialway','industrial','barrier','sport','building') def main(): a=argparse.ArgumentParser();a.add_argument('--osm',required=True);a.add_argument('--catalog',required=True);a.add_argument('--csv',required=True);a.add_argument('--json',required=True);a.add_argument('--samples',type=int,default=5);args=a.parse_args() with open(args.catalog,encoding='utf-8-sig',newline='') as f:catalog={r['code'].lower():r for r in csv.DictReader(f)} stats=defaultdict(Counter); samples=defaultdict(list); counts=Counter(); bad=[]; overview=Counter() opener=gzip.open if args.osm.endswith('.gz') else open with opener(args.osm,'rb') as f: for _,el in iterparse(f,events=('end',)): if el.tag not in ('node','way','relation'):continue if el.tag!='node':el.clear();continue tags={x.get('k'):x.get('v') for x in el.findall('tag') if x.get('k') is not None} if tags.get('garmin:feature')!='poi':el.clear();continue code=tags.get('garmin:type','').lower();counts[code]+=1;overview['pois']+=1 name=tags.get('name','');kind=[f'{k}={tags[k]}' for k in CLASS_KEYS if k in tags] if not kind:bad.append((code,name,el.get('lat'),el.get('lon'),'no_osm_semantic_tag'));overview['unclassified']+=1 if not name:overview['nameless']+=1 if tags.get('bgm:generated_name')=='yes':stats[code]['generated_name']+=1;overview['generated_name']+=1 if tags.get('bgm:synthetic_icon')=='yes':stats[code]['synthetic_icon']+=1;overview['synthetic_icon']+=1 if not tags.get('bgm_poi'):overview['missing_raw_type_index_tag']+=1 if len(samples[code])