v09
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"status": "PASS",
|
||||
"counts": {
|
||||
"pois": 101,
|
||||
"has_native_semantics": 43,
|
||||
"generated_name": 42,
|
||||
"has_bgm_poi_tag": 101,
|
||||
"needs_custom_style": 58
|
||||
},
|
||||
"catalog_types": 13,
|
||||
"exported_types": 13,
|
||||
"note": "POIs without standard OSM categories are valid for this PRIVATE map because bgm_poi uses custom index and renderer. They require importing the style; this does not prove OBF persistence or screen visibility.",
|
||||
"errors": [],
|
||||
"missing_types_from_catalog": []
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
mapset,kind,code,lat,lon,name
|
||||
"02234083","classic","0x6614",41.7204094,23.3722329,"Скала - 3310ACQWEHI"
|
||||
"02234083","classic","0x6616",41.7381334,23.3741426,"Гергийца (2589)"
|
||||
"02234083","classic","0x660c",41.7340136,23.3734131,"Порта"
|
||||
"02234083","classic","0x6616",41.7392492,23.3764601,"Безимен (2571)"
|
||||
"02234083","classic","0x6616",41.7404938,23.3828115,"Безимен (2554)"
|
||||
"02234083","classic","0x6616",41.7317605,23.3725762,"Безимен (2543)"
|
||||
"02234083","classic","0x6615",41.7433262,23.3739281,"Дупка"
|
||||
"02234083","classic","0x660c",41.7268896,23.3712673,"Синанишка порта"
|
||||
"02234083","classic","0x6600",41.7258811,23.3701301,"2467"
|
||||
"02234083","classic","0x6616",41.7444634,23.3775759,"Муратова чука (2449)"
|
||||
"02234083","classic","0x6503",41.7358160,23.3705378,"Рзкл"
|
||||
"02234083","classic","0x6414",41.7449141,23.3817816,"Вода (поток)"
|
||||
"02234083","classic","0x2800",41.7440128,23.3833480,"Гергийца"
|
||||
"02234083","classic","0x6600",41.7351937,23.4019947,"2592"
|
||||
"02234083","classic","0x6600",41.7336702,23.4037971,"2609"
|
||||
"02234083","classic","0x6616",41.7310739,23.4095478,"(2636)"
|
||||
"02234083","classic","0x660c",41.7308378,23.4024239,"Седл Малко-Голямо Спано поле-2527"
|
||||
"02234083","classic","0x6616",41.7301941,23.3993340,"Спанополски чукар (2576)"
|
||||
"02234083","classic","0x6600",41.7301941,23.3989048,"2576"
|
||||
"02234083","classic","0x6602",41.7262030,23.4098053,"Панорама Башлийски и Бъндеришки чукар"
|
||||
"02234083","classic","0x6614",41.7244649,23.4084320,"Скала - ZZC90NYTOCF"
|
||||
"02234083","classic","0x6600",41.7223191,23.4024024,"2404"
|
||||
"02234083","classic","0x650b",41.7234993,23.3853221,"Зимна маркировка - QRY7DY72FMJ"
|
||||
"02234083","classic","0x650b",41.7258811,23.3845925,"Зимна маркировка - 8JT9VM2UB7C"
|
||||
"02234083","classic","0x650b",41.7268896,23.3845067,"Зимна маркировка - L9KQU03N0Y5"
|
||||
"02234083","classic","0x650b",41.7277479,23.3846354,"Зимна маркировка - BRWNXVGHJ2A"
|
||||
"02234083","classic","0x650b",41.7285633,23.3850217,"Зимна маркировка - QCHZ1FXG1S6"
|
||||
"02234083","classic","0x650b",41.7290139,23.3851933,"Зимна маркировка - L9UJHM8M3L5"
|
||||
"02234083","classic","0x650b",41.7302155,23.3854723,"Зимна маркировка - ZLGO6R0CWUL"
|
||||
"02234083","classic","0x6511",41.7302585,23.3907509,"Вода - Извор - SZ7MXII06CB"
|
||||
"02234083","classic","0x650b",41.7324471,23.3863521,"Зимна маркировка - 0PO1Y4QNTKQ"
|
||||
"02234083","classic","0x650b",41.7333913,23.3868670,"Зимна маркировка - S0OOBD9DMT9"
|
||||
"02234083","classic","0x650b",41.7340779,23.3873820,"Зимна маркировка - ZJJ69H95896"
|
||||
"02234083","classic","0x650b",41.7346787,23.3876395,"Зимна маркировка - LRUKQQLW46P"
|
||||
"02234083","classic","0x650b",41.7201734,23.3826399,"Зимна маркировка - ECDE8HOEXT6"
|
||||
"02234083","classic","0x650b",41.7204738,23.3879399,"Зимна маркировка - BJH9BWY0S43"
|
||||
"02234083","classic","0x650b",41.7204738,23.3852148,"Зимна маркировка - EON21NTVSS2"
|
||||
"02234083","classic","0x650b",41.7205381,23.3832836,"Зимна маркировка - BJ2IFV25F7B"
|
||||
"02234083","classic","0x650b",41.7206669,23.3861375,"Зимна маркировка - KN9AQOZ1709"
|
||||
"02234083","classic","0x650b",41.7206669,23.3843994,"Зимна маркировка - UPFR04SCKJ6"
|
||||
"02234083","classic","0x6503",41.7207956,23.3865023,"Рзкл жълта/кафява маркировка"
|
||||
"02234083","classic","0x650b",41.7208600,23.3870173,"Зимна маркировка - ETSMTX323F0"
|
||||
"02234083","classic","0x650b",41.7209244,23.3838630,"Зимна маркировка - 2CH7W5QYBST"
|
||||
"02234083","classic","0x650b",41.7210317,23.3819532,"Зимна маркировка - 0H20QTLK2D8"
|
||||
"02234083","classic","0x650b",41.7217612,23.3817601,"Зимна маркировка - UR9S90UU4PI"
|
||||
"02234083","classic","0x650b",41.7224050,23.3814812,"Зимна маркировка - 0WITPVLXNZ5"
|
||||
"02234083","classic","0x650b",41.7226624,23.3854723,"Зимна маркировка - KKJ6VOB1UHB"
|
||||
"02234083","classic","0x6503",41.7229199,23.3789277,"Рзкл зимна"
|
||||
"02234083","classic","0x640a",41.7336702,23.4035397,"Опасно слизане"
|
||||
"02234083","classic","0x2800",41.7288423,23.3857727,"Големо Спано поле"
|
||||
"02234083","classic","0x2800",41.7323613,23.4036040,"Караулите"
|
||||
"02234083","classic","0x2800",41.7231774,23.4089899,"Малко Спано поле"
|
||||
"02234083","classic","0x6600",41.7415237,23.3972311,"2501"
|
||||
"02234083","classic","0x660c",41.7417812,23.3968449,"Бъндеришка порта"
|
||||
"02234083","classic","0x6600",41.7420816,23.3896351,"2468"
|
||||
"02234083","classic","0x6600",41.7421889,23.4056425,"2351"
|
||||
"02234083","classic","0x6600",41.7431545,23.4037542,"2314"
|
||||
"02234083","classic","0x6600",41.7439270,23.4045053,"2304"
|
||||
"02234083","classic","0x6600",41.7442489,23.4093547,"2229"
|
||||
"02234083","classic","0x6616",41.7449570,23.3969307,"Муратов връх (2669)"
|
||||
"02234083","classic","0x6600",41.7452145,23.4076166,"2241"
|
||||
"02234083","classic","0x6600",41.7461371,23.4070802,"2248"
|
||||
"02234083","classic","0x660c",41.7486048,23.3911800,"Муратова премка"
|
||||
"02234083","classic","0x660c",41.7495489,23.3981752,"Влахински превал"
|
||||
"02234083","classic","0x660c",41.7514372,23.3985186,"Влахински превал"
|
||||
"02234083","classic","0x6616",41.7519951,23.3843350,"Гредаро (2605)"
|
||||
"02234083","classic","0x6602",41.7353010,23.4009218,"Иглата на Караулите"
|
||||
"02234083","classic","0x6600",41.7354727,23.4006214,"2586"
|
||||
"02234083","classic","0x6600",41.7358804,23.3998060,"2578"
|
||||
"02234083","classic","0x6600",41.7360520,23.3994627,"2597"
|
||||
"02234083","classic","0x6600",41.7374468,23.4049559,"2504"
|
||||
"02234083","classic","0x6503",41.7538404,23.3998060,"Рзкл зимно ребро от Муратово езеро"
|
||||
"02234083","classic","0x6511",41.7506433,23.3959866,"Вода - Извор - D7562E5MBLA"
|
||||
"02234083","classic","0x650b",41.7363524,23.3882833,"Зимна маркировка - ML7GHHLGGVB"
|
||||
"02234083","classic","0x6503",41.7493987,23.3964372,"Кръстопът - JS2LQVK6Z0Y"
|
||||
"02234083","classic","0x6503",41.7450428,23.4058356,"Рзкл Муратово ез."
|
||||
"02234083","classic","0x650b",41.7410517,23.3955145,"Зимна маркировка - ZVN3B0ZG6Z7"
|
||||
"02234083","classic","0x650b",41.7415667,23.3965015,"Зимна маркировка - C0GJITD9Q3J"
|
||||
"02234083","classic","0x6511",41.7416954,23.3946991,"Вода - Извор - YY24QUNHLSL"
|
||||
"02234083","classic","0x650b",41.7419100,23.3969736,"Зимна маркировка - N2XVWARW03F"
|
||||
"02234083","classic","0x650b",41.7400217,23.3932185,"Зимна маркировка - T5WTMHMWT4K"
|
||||
"02234083","classic","0x650b",41.7395926,23.3919525,"Зимна маркировка - UFCHM8SJ1D7"
|
||||
"02234083","classic","0x650b",41.7391205,23.3905363,"Зимна маркировка - O9IF7BPSJR6"
|
||||
"02234083","classic","0x650b",41.7378759,23.3894420,"Зимна маркировка - XCWILHV9I1K"
|
||||
"02234083","classic","0x6503",41.7445707,23.4080887,"Рзкл Муратово езеро"
|
||||
"02234083","classic","0x6414",41.7549562,23.3888626,"Вода - Чешма - XSX8SGXW0S5"
|
||||
"02234083","classic","0x6414",41.7544627,23.3887553,"Вода - Чешма - X8HNWYW73EC"
|
||||
"02234083","classic","0x6415",41.7402792,23.3953857,"Бивак"
|
||||
"02234083","classic","0x640a",41.7359018,23.3995056,"Опасно слизане"
|
||||
"02234083","classic","0x6415",41.7453432,23.4067583,"Бивак"
|
||||
"02234083","classic","0x6414",41.7509651,23.4011364,"Вода - Чешма - 4DAF49MGS8O"
|
||||
"02234083","classic","0x640a",41.7456007,23.3963728,"Опасно слизане"
|
||||
"02234083","classic","0x6414",41.7461586,23.4052134,"Вода (поток)"
|
||||
"02234083","classic","0x6414",41.7388630,23.3864164,"Вода - Чешма - 6B4RAQRPAHT"
|
||||
"02234083","classic","0x6414",41.7479610,23.4026814,"Вода - Чешма - B107V8QHR57"
|
||||
"02234083","classic","0x6414",41.7468023,23.3872318,"Вода (поток)"
|
||||
"02234083","classic","0x6415",41.7465878,23.4065437,"Бивак"
|
||||
"02234083","classic","0x2800",41.7509651,23.3984327,"Влахински превал"
|
||||
"02234083","classic","0x2800",41.7442060,23.4046984,"Муратов циркус"
|
||||
"02234083","classic","0x2800",41.7368460,23.3897853,"Циркус Големо Спано поле"
|
||||
"02234083","classic","0x2800",41.7449570,23.4051275,"Овинати езера"
|
||||
|
@@ -0,0 +1,14 @@
|
||||
code,kind,count,named,default_tags,has_typ_icon,configured_rules,example1_lat,example1_lon,example1_name,example1_map,example2_lat,example2_lon,example2_name,example2_map,example3_lat,example3_lon,example3_name,example3_map,example4_lat,example4_lon,example4_name,example4_map,example5_lat,example5_lon,example5_name,example5_map
|
||||
"0x2800",classic,8,8,"place=locality",no,1,41.7440128,23.3833480,"Гергийца","02234083",41.7509651,23.3984327,"Влахински превал","02234083",41.7288423,23.3857727,"Големо Спано поле","02234083",41.7231774,23.4089899,"Малко Спано поле","02234083",41.7442060,23.4046984,"Муратов циркус","02234083"
|
||||
"0x640a",classic,3,3,"garmin:legend=danger",yes,1,41.7456007,23.3963728,"Опасно слизане","02234083",41.7359018,23.3995056,"Опасно слизане","02234083",41.7336702,23.4035397,"Опасно слизане","02234083",,,,,,,,
|
||||
"0x6414",classic,8,8,"garmin:legend=drinking_water;amenity=drinking_water",yes,1,41.7544627,23.3887553,"Вода - Чешма - X8HNWYW73EC","02234083",41.7388630,23.3864164,"Вода - Чешма - 6B4RAQRPAHT","02234083",41.7468023,23.3872318,"Вода (поток)","02234083",41.7509651,23.4011364,"Вода - Чешма - 4DAF49MGS8O","02234083",41.7449141,23.3817816,"Вода (поток)","02234083"
|
||||
"0x6415",classic,3,3,"garmin:legend=camp_site;tourism=camp_site",yes,1,41.7402792,23.3953857,"Бивак","02234083",41.7453432,23.4067583,"Бивак","02234083",41.7465878,23.4065437,"Бивак","02234083",,,,,,,,
|
||||
"0x6503",classic,7,7,"garmin:legend=crossroad",yes,1,41.7538404,23.3998060,"Рзкл зимно ребро от Муратово езеро","02234083",41.7445707,23.4080887,"Рзкл Муратово езеро","02234083",41.7358160,23.3705378,"Рзкл","02234083",41.7493987,23.3964372,"Кръстопът - JS2LQVK6Z0Y","02234083",41.7229199,23.3789277,"Рзкл зимна","02234083"
|
||||
"0x650b",classic,31,31,"garmin:legend=winter_marking",yes,1,41.7419100,23.3969736,"Зимна маркировка - N2XVWARW03F","02234083",41.7285633,23.3850217,"Зимна маркировка - QCHZ1FXG1S6","02234083",41.7205381,23.3832836,"Зимна маркировка - BJ2IFV25F7B","02234083",41.7395926,23.3919525,"Зимна маркировка - UFCHM8SJ1D7","02234083",41.7324471,23.3863521,"Зимна маркировка - 0PO1Y4QNTKQ","02234083"
|
||||
"0x6511",classic,3,3,"garmin:legend=spring;natural=spring",yes,1,41.7506433,23.3959866,"Вода - Извор - D7562E5MBLA","02234083",41.7416954,23.3946991,"Вода - Извор - YY24QUNHLSL","02234083",41.7302585,23.3907509,"Вода - Извор - SZ7MXII06CB","02234083",,,,,,,,
|
||||
"0x6600",classic,17,17,"garmin:legend=triangulation",yes,1,41.7415237,23.3972311,"2501","02234083",41.7421889,23.4056425,"2351","02234083",41.7442489,23.4093547,"2229","02234083",41.7452145,23.4076166,"2241","02234083",41.7336702,23.4037971,"2609","02234083"
|
||||
"0x6602",classic,2,2,"garmin:legend=viewpoint;tourism=viewpoint",yes,1,41.7262030,23.4098053,"Панорама Башлийски и Бъндеришки чукар","02234083",41.7353010,23.4009218,"Иглата на Караулите","02234083",,,,,,,,,,,,
|
||||
"0x660c",classic,7,7,"natural=saddle",yes,1,41.7514372,23.3985186,"Влахински превал","02234083",41.7417812,23.3968449,"Бъндеришка порта","02234083",41.7495489,23.3981752,"Влахински превал","02234083",41.7486048,23.3911800,"Муратова премка","02234083",41.7340136,23.3734131,"Порта","02234083"
|
||||
"0x6614",classic,2,2,"garmin:legend=rock",yes,1,41.7204094,23.3722329,"Скала - 3310ACQWEHI","02234083",41.7244649,23.4084320,"Скала - ZZC90NYTOCF","02234083",,,,,,,,,,,,
|
||||
"0x6615",classic,1,1,"garmin:legend=cave;natural=cave_entrance",yes,1,41.7433262,23.3739281,"Дупка","02234083",,,,,,,,,,,,,,,,
|
||||
"0x6616",classic,9,9,"natural=peak",no,1,41.7404938,23.3828115,"Безимен (2554)","02234083",41.7381334,23.3741426,"Гергийца (2589)","02234083",41.7317605,23.3725762,"Безимен (2543)","02234083",41.7444634,23.3775759,"Муратова чука (2449)","02234083",41.7301941,23.3993340,"Спанополски чукар (2576)","02234083"
|
||||
|
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"source_pois": 101,
|
||||
"expected_after_drops": 101,
|
||||
"declared_drops": 0,
|
||||
"osm_pois": 101,
|
||||
"missing": 0,
|
||||
"extra": 0,
|
||||
"rule_tag_violations": {},
|
||||
"examples": [],
|
||||
"status": "PASS",
|
||||
"limitation": "Same IMG decoder is used for manifest and OSM: this validates export fidelity, not ground truth or OBF persistence."
|
||||
}
|
||||
Binary file not shown.
@@ -84,7 +84,7 @@ def write_xml(tree,path):
|
||||
tree.write(path,encoding='utf-8',xml_declaration=True)
|
||||
ET.parse(path)
|
||||
|
||||
def make_configs(poi_base,render_base,catalog,folder,threshold,frequent_zoom=15,rare_zoom=17):
|
||||
def make_configs(poi_base,render_base,catalog,folder,threshold,frequent_zoom=15,rare_zoom=17,route_styles=()):
|
||||
# Keep entire upstream definitions! Replacing with a standalone tiny XML is
|
||||
# precisely what used to make ordinary semantic POIs disappear.
|
||||
poi=ET.parse(poi_base); proot=poi.getroot()
|
||||
@@ -108,10 +108,15 @@ def make_configs(poi_base,render_base,catalog,folder,threshold,frequent_zoom=15,
|
||||
code=r['code']
|
||||
ET.SubElement(target,'type',tag='bgm_poi',value='type_'+code,
|
||||
minzoom=str(frequent_zoom) if r['count']>=threshold else str(rare_zoom))
|
||||
rp=folder/'rendering_types.bgmountains.xml'
|
||||
# Route overlays have their OWN map-index main types, not only an ignored metadata tag.
|
||||
# Do not map decorative overlays to 'highway=path': that would create bogus routing.
|
||||
for route in route_styles:
|
||||
if route.get('show','yes').lower() not in ('yes','1','true'): continue
|
||||
ET.SubElement(target,'type',tag='bgm_route',value='type_'+route['code'],minzoom=route['display_minzoom'])
|
||||
rp=folder/'rendering_types.bgmountains.xml'
|
||||
write_xml(rendering,rp)
|
||||
summary={
|
||||
'catalog_types':len(catalog),
|
||||
'catalog_types':len(catalog), 'route_overlay_types':len(route_styles),
|
||||
'poi_count_in_catalog':sum(r.get('sample_count',r['count']) for r in catalog),
|
||||
'types_at_or_above_threshold':sum(r['count']>=threshold for r in catalog),
|
||||
'frequent_index_minzoom':frequent_zoom, 'rare_index_minzoom':rare_zoom,
|
||||
@@ -124,10 +129,26 @@ def make_configs(poi_base,render_base,catalog,folder,threshold,frequent_zoom=15,
|
||||
(folder/'type-index-summary.json').write_text(json.dumps(summary,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
return pa,rp,summary
|
||||
|
||||
def make_style(icon_csv, folder, catalog):
|
||||
"""Optional addon for otherwise unclassified types using preinstalled OsmAnd icons.
|
||||
def read_route_styles(path):
|
||||
with Path(path).open(encoding='utf-8-sig',newline='') as f: rows=list(csv.DictReader(f))
|
||||
if not rows or not {'code','color','show','display_minzoom','width'}.issubset(rows[0]):
|
||||
raise SystemExit('Invalid trail-styles.csv: '+str(path))
|
||||
seen=set()
|
||||
for r in rows:
|
||||
code='0x'+format(int(r['code'],0),'x')
|
||||
if code in seen: raise SystemExit('Duplicate route style type: '+code)
|
||||
seen.add(code);r['code']=code
|
||||
if not (len(r['color'])==7 and r['color'][0]=='#' and all(c in '0123456789abcdefABCDEF' for c in r['color'][1:])):
|
||||
raise SystemExit('Invalid CSS color in route type '+code)
|
||||
if not (0.2<=float(r['width'])<=15 and 1<=int(r['display_minzoom'])<=21):
|
||||
raise SystemExit('Invalid route width/zoom '+code)
|
||||
return rows
|
||||
|
||||
This cannot install Garmin bitmap icons and must be enabled in OsmAnd.
|
||||
def make_style(icon_csv,folder,catalog,route_styles=()):
|
||||
"""Selectable OsmAnd map style that maps BGM indexes to built-in icons.
|
||||
|
||||
The .obf itself only embeds geometry/tags. Android requires installing and
|
||||
selecting this renderer; stock app ignores bgm_poi and bgm_route styling.
|
||||
"""
|
||||
known={r['code'] for r in catalog}
|
||||
with Path(icon_csv).open(encoding='utf-8-sig',newline='') as f:
|
||||
@@ -135,26 +156,37 @@ def make_style(icon_csv, folder, catalog):
|
||||
visible=[]
|
||||
for r in rows:
|
||||
code='0x'+format(int(r['code'],0),'x')
|
||||
if code not in known:continue # bbox builds are smaller than national config
|
||||
if r['show_in_custom_style'].strip().lower() not in ('yes','true','1'):continue
|
||||
if code not in known or r['show_in_custom_style'].strip().lower() not in ('yes','true','1'):
|
||||
continue
|
||||
name=r['osmand_builtin_icon'].strip()
|
||||
if not name or not name.replace('_','').replace('-','').isalnum():
|
||||
raise SystemExit('Invalid OsmAnd icon in '+str(icon_csv)+' for '+code)
|
||||
raise SystemExit('Invalid OsmAnd icon: '+repr(name)+' code='+code)
|
||||
zoom=int(r['minzoom'])
|
||||
if not 1<=zoom<=21:raise SystemExit('Invalid icon zoom for '+code)
|
||||
if not 1<=zoom<=21: raise SystemExit('Bad icon zoom: '+code)
|
||||
visible.append((code,name,zoom))
|
||||
root=ET.Element('renderingStyle',name='BGMountains custom POIs',depends='default',
|
||||
defaultColor='#fff8ee',version='1')
|
||||
order=ET.SubElement(root,'order')
|
||||
points=ET.SubElement(root,'point')
|
||||
root=ET.Element('renderingStyle',name='BGMountains outdoor v0.9',
|
||||
depends='default',defaultColor='#fff8ee',version='1')
|
||||
order=ET.SubElement(root,'order');points=ET.SubElement(root,'point')
|
||||
lines=ET.SubElement(root,'line')
|
||||
for code,name,zoom in visible:
|
||||
ET.SubElement(order,'case',tag='bgm_poi',value='type_'+code,minzoom=str(zoom),
|
||||
point='true',objectType='1',order='128')
|
||||
ET.SubElement(order,'case',tag='bgm_poi',value='type_'+code,
|
||||
minzoom=str(zoom),point='true',objectType='1',order='120')
|
||||
ET.SubElement(points,'case',tag='bgm_poi',value='type_'+code,minzoom=str(zoom),
|
||||
icon=name,iconOrder='10',iconVisibleSize='12')
|
||||
path=folder/'BGMountains-custom.render.xml'
|
||||
icon=name,iconOrder='11',iconVisibleSize='16',intersectionSizeFactor='0.7')
|
||||
route_count=0
|
||||
for route in route_styles:
|
||||
if route.get('show','yes').lower() not in ('yes','true','1'):continue
|
||||
route_count+=1
|
||||
code=route['code'];zoom=route['display_minzoom']
|
||||
ET.SubElement(order,'case',tag='bgm_route',value='type_'+code,
|
||||
minzoom=zoom,objectType='2',order='125')
|
||||
# Color and width faithfully recover TYP base line color, but NOT its bitmap
|
||||
# segment pattern. A precise pattern renderer requires a custom app build.
|
||||
ET.SubElement(lines,'case',tag='bgm_route',value='type_'+code,
|
||||
minzoom=zoom,color=route['color'],strokeWidth=route['width'],cap='ROUND')
|
||||
path=folder/'BGMountains-outdoor.render.xml'
|
||||
write_xml(ET.ElementTree(root),path)
|
||||
return path,len(visible)
|
||||
return path,len(visible),route_count
|
||||
|
||||
def obtain_creator(cache, offline=False):
|
||||
"""Opt-in download from official OsmAnd nightly build (large file)."""
|
||||
@@ -210,7 +242,8 @@ def main():
|
||||
p.add_argument('--threshold',type=int,default=100,help='Frequent-type occurrence threshold')
|
||||
p.add_argument('--frequent-zoom',type=int,default=15,help='Store frequent raw map icons at zoom N and above')
|
||||
p.add_argument('--rare-zoom',type=int,default=17,help='Store rare raw map icons at zoom N and above')
|
||||
p.add_argument('--icons-config',default=str(ROOT/'config/osmand-icons.csv'),help='OsmAnd built-in icon choices for custom addon style')
|
||||
p.add_argument('--icons-config',default=str(ROOT/'config/osmand-icons.csv'),help='OsmAnd icon mappings')
|
||||
p.add_argument('--trail-styles',default=str(ROOT/'config/trail-styles.csv'),help='Garmin TYP coloured route overlay styles')
|
||||
p.add_argument('--poi-overrides',default=str(ROOT/'config/poi-overrides-all-types.csv'))
|
||||
p.add_argument('--poi-name-fixes',default=str(ROOT/'config/poi-name-fixes.csv'))
|
||||
p.add_argument('--poi-labels',default=str(ROOT/'config/poi-labels-bg.csv'),help='Bulgarian prefixes for stable generated names on nameless Garmin POIs')
|
||||
@@ -272,9 +305,10 @@ def main():
|
||||
folder=dest/'osmand-config';folder.mkdir(exist_ok=True)
|
||||
poi_base=base_xml(args.poi_base,creator,'poi',dest/'upstream',args.offline)
|
||||
rendering_base=base_xml(args.rendering_base,creator,'rendering',dest/'upstream',args.offline)
|
||||
poi_cfg,render_cfg,summary=make_configs(poi_base,rendering_base,rows,folder,args.threshold,args.frequent_zoom,args.rare_zoom)
|
||||
style,nicons=make_style(args.icons_config,folder,rows)
|
||||
print('Indexed POI types:',summary['catalog_types'],'frequent:',summary['types_at_or_above_threshold'],'custom style icons:',nicons,flush=True)
|
||||
route_styles=read_route_styles(args.trail_styles)
|
||||
poi_cfg,render_cfg,summary=make_configs(poi_base,rendering_base,rows,folder,args.threshold,args.frequent_zoom,args.rare_zoom,route_styles)
|
||||
style,nicons,nlines=make_style(args.icons_config,folder,rows,route_styles)
|
||||
print('Indexed POI types:',summary['catalog_types'],'frequent:',summary['types_at_or_above_threshold'],'custom icons:',nicons,'colored route types:',nlines,flush=True)
|
||||
if args.prepare_only:
|
||||
print('PREPARED (no OBF generated). Files at:',dest)
|
||||
return 0
|
||||
@@ -299,7 +333,7 @@ def main():
|
||||
'created_at_utc':dt.datetime.now(dt.timezone.utc).isoformat(),
|
||||
'obf':str(built),'obf_bytes':built.stat().st_size,
|
||||
'input_osm':str(osm),'poi_types':str(poi_cfg),'rendering_types':str(render_cfg),
|
||||
'custom_style':str(style), 'custom_style_icon_rules':nicons,
|
||||
'custom_style':str(style), 'custom_style_icon_rules':nicons, 'custom_route_rules':nlines,
|
||||
'summary':summary
|
||||
},ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
return 0
|
||||
|
||||
+11
-6
@@ -23,9 +23,13 @@ Options parse(int argc,char**argv){Options o;for(int i=1;i<argc;i++){std::string
|
||||
if(o.input.empty())fail("--input required");if(o.output.empty()&&!o.inspect&&o.poiTypesOut.empty()&&o.poiManifest.empty()&&o.iconsDir.empty())fail("--output, --inspect or an inventory output required");if(!o.poiAdditions.empty()&&o.output.empty())fail("--poi-additions requires --output");if(o.jobs<1||o.jobs>64)fail("--jobs must be 1..64");if(o.examples<1||o.examples>20)fail("--poi-examples must be 1..20");if(o.clip&&(!o.bbox.valid()||o.bbox.w< -180||o.bbox.e>180||o.bbox.s< -90||o.bbox.n>90))fail("Invalid bbox");return o;}
|
||||
|
||||
Tags lineTags(int t,bool ext,const std::string&name){Tags v;tag(v,"garmin:type",code(t));tag(v,"garmin:feature",ext?"extended_polyline":"polyline");tag(v,"name",name);
|
||||
if(ext){ // Physical trail identity not proven: preserve Garmin overlay, mark it as a hiking path geometry.
|
||||
tag(v,"highway","path");tag(v,"foot","yes");tag(v,"trailblazed","yes");tag(v,"garmin:route_overlay","yes");
|
||||
const auto it=gStyles.lines.find(t);if(it!=gStyles.lines.end()&&it->second.color)tag(v,"colour",hexColor(it->second.color));return v;
|
||||
if(ext){ // TYP route overlay is graphical, NOT independently verified routable geometry.
|
||||
tag(v,"bgm_route","type_"+code(t));tag(v,"garmin:route_overlay","yes");
|
||||
const auto it=gStyles.lines.find(t);if(it!=gStyles.lines.end()){
|
||||
if(it->second.color)tag(v,"colour",hexColor(it->second.color));
|
||||
if(it->second.hasPattern())tag(v,"bgm:line_pattern","garmin_bitmap");
|
||||
}
|
||||
return v;
|
||||
}
|
||||
if(t<=0x0c){const char*klass=nullptr;
|
||||
switch(t){case 0:klass="road";break;case 1:klass="motorway";break;case 2:klass="primary";break;case 3:klass="secondary";break;case 4:klass="tertiary";break;case 5:klass="unclassified";break;case 6:klass="residential";break;case 7:klass="service";break;case 8:case 9:case 11:klass="motorway_link";break;case 10:klass="track";break;case 12:klass="residential";break;}
|
||||
@@ -44,7 +48,7 @@ Tags areaTags(int t,const std::string&name){Tags v;tag(v,"garmin:type",code(t));
|
||||
case 0x05:tag(v,"amenity","parking");break;case 0x07:tag(v,"aeroway","aerodrome");break;
|
||||
case 0x0b:tag(v,"amenity","hospital");break;case 0x0c:tag(v,"landuse","industrial");break;
|
||||
case 0x13:tag(v,"building","yes");break;case 0x17:tag(v,"leisure","park");break;
|
||||
case 0x18:tag(v,"leisure","golf_course");break;case 0x09:tag(v,"leisure","marina");break;
|
||||
case 0x18:tag(v,"leisure","sports_centre");break;case 0x09:tag(v,"landuse","retail");break;
|
||||
case 0x14:case 0x15:case 0x16:tag(v,"boundary","national_park");break;
|
||||
case 0x28:case 0x32:tag(v,"natural","water");tag(v,"water","sea");break;
|
||||
case 0x3c:case 0x3d:case 0x40:case 0x41:tag(v,"natural","water");break;
|
||||
@@ -69,13 +73,14 @@ Tags poiTags(int t,const std::string&name){Tags v;tag(v,"garmin:type",code(t));t
|
||||
else if(t==0x6407){tag(v,"tourism","picnic_site");}
|
||||
else if(t==0x640d){tag(v,"aerialway","station");}
|
||||
else if(t==0x6404||t==0x640b||t==0x641a||t==0x2c0b){tag(v,"amenity","place_of_worship");}
|
||||
else if(t==0x6602||t==0x6608){tag(v,"tourism","viewpoint");}
|
||||
else if(t==0x6602){tag(v,"tourism","viewpoint");}
|
||||
else if(t==0x6608){tag(v,"tourism","hotel");}
|
||||
else if(t==0x2800){tag(v,"place","locality");}
|
||||
else if(t==0x6600){ // Geographic spot heights; don't misclassify elevations as villages.
|
||||
try{size_t parsed=0;int n=std::stoi(name,&parsed);if(parsed==name.size()&&n>=0&&n<=8848)tag(v,"ele",std::to_string(n));}catch(...){}
|
||||
}
|
||||
else if(t==0x6614){tag(v,"garmin:marker","distance_or_index");}
|
||||
else if(t==0x6500){tag(v,"man_made","water_works");}
|
||||
else if(t==0x6500){tag(v,"man_made","water_intake");}
|
||||
else if(t==0x6402&&name.find("Посетителски")!=std::string::npos){tag(v,"tourism","information");tag(v,"information","office");}
|
||||
else if(t==0x6411){tag(v,"man_made","tower");}
|
||||
// 0x6414 handled above as drinking_water: conflicting old water_well branch removed.
|
||||
|
||||
@@ -14,13 +14,13 @@ class TestLocalDisplay(unittest.TestCase):
|
||||
c=x['code'];self.assertEqual(r[c]['mode'],'replace',c)
|
||||
self.assertTrue(names[c],c)
|
||||
t=r[c]['tags']
|
||||
self.assertTrue(any(a+'=' in t for a in ['amenity','shop','tourism','natural','man_made','leisure','historic','place','building','emergency','power','highway','waterway','sport','industrial']),c)
|
||||
self.assertTrue(t,c) # BGMountains custom POI index handles non-standard types
|
||||
self.assertNotIn('tags,,',t)
|
||||
def test_key_places(self):
|
||||
r={x['code']:x for x in load('config/poi-overrides-all-types.csv')}
|
||||
self.assertIn('man_made=water_intake',r['0x6500']['tags'])
|
||||
self.assertIn('bgm:synthetic_icon=yes',r['0x6500']['tags'])
|
||||
self.assertIn('tourism=information',r['0x6500']['tags'])
|
||||
self.assertNotIn('bgm:synthetic_icon=yes',r['0x6500']['tags'])
|
||||
self.assertNotIn('tourism=information',r['0x6500']['tags'])
|
||||
self.assertIn('tourism=viewpoint',r['0x6602']['tags'])
|
||||
self.assertNotIn('bgm:synthetic_icon',r['0x6602']['tags'])
|
||||
def test_all_legacy_codes_retained_for_review(self):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit whether IMG-derived POIs have both OSM display tags and names.
|
||||
"""Audit IMG-derived POIs for custom indexability and truthfully report native semantic tags.
|
||||
|
||||
This measures converted OSM, NOT actual OsmAnd screen visibility nor .obf indexing.
|
||||
Output inventory always includes Garmin codes and example coordinates.
|
||||
@@ -23,18 +23,20 @@ def main():
|
||||
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 kind:overview['needs_custom_style']+=1
|
||||
else:overview['has_native_semantics']+=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
|
||||
else:overview['has_bgm_poi_tag']+=1
|
||||
if len(samples[code])<args.samples:samples[code].append((el.get('lat'),el.get('lon'),name, '|'.join(kind)))
|
||||
el.clear()
|
||||
rows=[]
|
||||
for code in sorted(counts,key=lambda x:(-counts[x],x)):
|
||||
base=catalog.get(code,{})
|
||||
row=dict(code=code,osm_poi_count=counts[code],nationwide_type_count=base.get('count',''),
|
||||
icon_strategy=('synthetic' if stats[code]['synthetic_icon'] else 'native-osm'),
|
||||
icon_strategy=('native+custom' if samples[code][0][3] else 'custom_style_required'),
|
||||
synthetic_count=stats[code]['synthetic_icon'],generated_name_count=stats[code]['generated_name'],
|
||||
legend=base.get('verified_legend_bg',''),review_status=base.get('review_status',''),
|
||||
example_lat=samples[code][0][0],example_lon=samples[code][0][1],
|
||||
@@ -44,11 +46,11 @@ def main():
|
||||
path=Path(args.csv);path.parent.mkdir(parents=True,exist_ok=True)
|
||||
with path.open('w',encoding='utf-8',newline='') as f:
|
||||
if rows:w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows)
|
||||
summary=dict(status='PASS' if not bad and not overview['nameless'] and not overview['missing_raw_type_index_tag'] else 'FAIL',
|
||||
summary=dict(status='PASS' if not overview['nameless'] and not overview['missing_raw_type_index_tag'] else 'FAIL',
|
||||
counts=dict(overview),catalog_types=len(catalog),exported_types=len(counts),
|
||||
note='Audit is for OSM tags only. A real .obf inspector comparison and OsmAnd visual inspection remain required.',
|
||||
note='POIs without standard OSM categories are valid for this PRIVATE map because bgm_poi uses custom index and renderer. They require importing the style; this does not prove OBF persistence or screen visibility.',
|
||||
errors=bad[:100],missing_types_from_catalog=sorted(set(counts)-set(catalog)))
|
||||
Path(args.json).write_text(json.dumps(summary,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print(json.dumps(summary,ensure_ascii=False,indent=2))
|
||||
return 0 if summary['status']=='PASS' and not summary['missing_types_from_catalog'] else 0
|
||||
return 0 if summary['status']=='PASS' and not summary['missing_types_from_catalog'] else 2
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
|
||||
Reference in New Issue
Block a user