Spaces:
Sleeping
Sleeping
feat: improve custom coordinate text layout (two-line title/subtitle)
Browse files- app.py +49 -14
- cities_data.py +9 -3
- create_map_poster.py +36 -29
app.py
CHANGED
|
@@ -376,7 +376,7 @@ def update_cities(country, province, lang="en"):
|
|
| 376 |
|
| 377 |
def update_districts(country, province, city, lang="en"):
|
| 378 |
"""Update district dropdown based on city selection."""
|
| 379 |
-
if country != "中国":
|
| 380 |
return gr.update(choices=[], value=None, visible=False)
|
| 381 |
|
| 382 |
lang_code = "en" if lang == "English" else "cn"
|
|
@@ -411,6 +411,20 @@ def create_interface():
|
|
| 411 |
else []
|
| 412 |
)
|
| 413 |
default_city_key = default_cities[0][1] if default_cities else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
default_theme = theme_choices[0][1] if theme_choices else "feature_based"
|
| 415 |
default_theme = theme_choices[0][1] if theme_choices else "feature_based"
|
| 416 |
|
|
@@ -504,16 +518,11 @@ def create_interface():
|
|
| 504 |
)
|
| 505 |
|
| 506 |
district_dropdown = gr.Dropdown(
|
| 507 |
-
choices=
|
| 508 |
-
|
| 509 |
-
default_province_key,
|
| 510 |
-
default_city_key,
|
| 511 |
-
default_lang_code,
|
| 512 |
-
),
|
| 513 |
-
value=default_city_key,
|
| 514 |
label="选择区县",
|
| 515 |
interactive=True,
|
| 516 |
-
visible=(default_country_key == "中国"),
|
| 517 |
)
|
| 518 |
|
| 519 |
with gr.Group(visible=False) as custom_coords_group:
|
|
@@ -528,11 +537,11 @@ def create_interface():
|
|
| 528 |
label="经度 (Longitude)", value=None, precision=6
|
| 529 |
)
|
| 530 |
custom_city_name = gr.Textbox(
|
| 531 |
-
label="
|
| 532 |
)
|
| 533 |
custom_country_name = gr.Textbox(
|
| 534 |
-
label="
|
| 535 |
-
placeholder="如: China",
|
| 536 |
value="",
|
| 537 |
)
|
| 538 |
|
|
@@ -629,6 +638,7 @@ def create_interface():
|
|
| 629 |
current_country,
|
| 630 |
current_province,
|
| 631 |
current_city,
|
|
|
|
| 632 |
current_theme,
|
| 633 |
current_layers,
|
| 634 |
):
|
|
@@ -649,6 +659,14 @@ def create_interface():
|
|
| 649 |
if current_city
|
| 650 |
else []
|
| 651 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 652 |
|
| 653 |
# Themes
|
| 654 |
new_theme_choices = get_theme_choices(lang_code)
|
|
@@ -691,8 +709,8 @@ def create_interface():
|
|
| 691 |
),
|
| 692 |
gr.update(
|
| 693 |
choices=new_districts,
|
| 694 |
-
value=
|
| 695 |
-
visible=(current_country == "中国"),
|
| 696 |
label="Select District" if lang == "English" else "选择区县",
|
| 697 |
),
|
| 698 |
gr.update(
|
|
@@ -729,12 +747,26 @@ def create_interface():
|
|
| 729 |
if lang == "English"
|
| 730 |
else "勾选后保留海报边缘背景",
|
| 731 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 732 |
gr.update(
|
| 733 |
value="🚀 Generate Poster" if lang == "English" else "🚀 生成海报"
|
| 734 |
),
|
| 735 |
gr.update(
|
| 736 |
label="📥 Download Poster" if lang == "English" else "📥 下载海报"
|
| 737 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
)
|
| 739 |
|
| 740 |
lang_radio.change(
|
|
@@ -744,6 +776,7 @@ def create_interface():
|
|
| 744 |
country_dropdown,
|
| 745 |
province_dropdown,
|
| 746 |
city_dropdown,
|
|
|
|
| 747 |
theme_dropdown,
|
| 748 |
layers_checkbox,
|
| 749 |
],
|
|
@@ -763,6 +796,8 @@ def create_interface():
|
|
| 763 |
show_text_checkbox,
|
| 764 |
generate_btn,
|
| 765 |
download_btn,
|
|
|
|
|
|
|
| 766 |
],
|
| 767 |
)
|
| 768 |
|
|
|
|
| 376 |
|
| 377 |
def update_districts(country, province, city, lang="en"):
|
| 378 |
"""Update district dropdown based on city selection."""
|
| 379 |
+
if country != "中国" or not city or city.endswith("_WHOLE"):
|
| 380 |
return gr.update(choices=[], value=None, visible=False)
|
| 381 |
|
| 382 |
lang_code = "en" if lang == "English" else "cn"
|
|
|
|
| 411 |
else []
|
| 412 |
)
|
| 413 |
default_city_key = default_cities[0][1] if default_cities else None
|
| 414 |
+
|
| 415 |
+
# Handle district initialization carefully to avoid "Value not in choices" error
|
| 416 |
+
default_districts = (
|
| 417 |
+
get_districts(
|
| 418 |
+
default_country_key,
|
| 419 |
+
default_province_key,
|
| 420 |
+
default_city_key,
|
| 421 |
+
default_lang_code,
|
| 422 |
+
)
|
| 423 |
+
if default_city_key
|
| 424 |
+
else []
|
| 425 |
+
)
|
| 426 |
+
default_district_key = default_districts[0][1] if default_districts else None
|
| 427 |
+
|
| 428 |
default_theme = theme_choices[0][1] if theme_choices else "feature_based"
|
| 429 |
default_theme = theme_choices[0][1] if theme_choices else "feature_based"
|
| 430 |
|
|
|
|
| 518 |
)
|
| 519 |
|
| 520 |
district_dropdown = gr.Dropdown(
|
| 521 |
+
choices=default_districts,
|
| 522 |
+
value=default_district_key,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
label="选择区县",
|
| 524 |
interactive=True,
|
| 525 |
+
visible=(default_country_key == "中国" and default_district_key is not None),
|
| 526 |
)
|
| 527 |
|
| 528 |
with gr.Group(visible=False) as custom_coords_group:
|
|
|
|
| 537 |
label="经度 (Longitude)", value=None, precision=6
|
| 538 |
)
|
| 539 |
custom_city_name = gr.Textbox(
|
| 540 |
+
label="主标题 (Main Title)", placeholder="如: Shanghai 或 '我们的家'"
|
| 541 |
)
|
| 542 |
custom_country_name = gr.Textbox(
|
| 543 |
+
label="副标题 (Subtitle)",
|
| 544 |
+
placeholder="如: China 或 '2024.10.20'",
|
| 545 |
value="",
|
| 546 |
)
|
| 547 |
|
|
|
|
| 638 |
current_country,
|
| 639 |
current_province,
|
| 640 |
current_city,
|
| 641 |
+
current_district,
|
| 642 |
current_theme,
|
| 643 |
current_layers,
|
| 644 |
):
|
|
|
|
| 659 |
if current_city
|
| 660 |
else []
|
| 661 |
)
|
| 662 |
+
|
| 663 |
+
# Ensure district value is valid for new choices
|
| 664 |
+
valid_district = None
|
| 665 |
+
if new_districts:
|
| 666 |
+
if any(v == current_district for d, v in new_districts):
|
| 667 |
+
valid_district = current_district
|
| 668 |
+
else:
|
| 669 |
+
valid_district = new_districts[0][1]
|
| 670 |
|
| 671 |
# Themes
|
| 672 |
new_theme_choices = get_theme_choices(lang_code)
|
|
|
|
| 709 |
),
|
| 710 |
gr.update(
|
| 711 |
choices=new_districts,
|
| 712 |
+
value=valid_district,
|
| 713 |
+
visible=(current_country == "中国" and valid_district is not None),
|
| 714 |
label="Select District" if lang == "English" else "选择区县",
|
| 715 |
),
|
| 716 |
gr.update(
|
|
|
|
| 747 |
if lang == "English"
|
| 748 |
else "勾选后保留海报边缘背景",
|
| 749 |
),
|
| 750 |
+
gr.update(
|
| 751 |
+
label="Show Text" if lang == "English" else "显示文字",
|
| 752 |
+
info="Show city name and coordinates on poster"
|
| 753 |
+
if lang == "English"
|
| 754 |
+
else "在海报上显示城市名和经纬度",
|
| 755 |
+
),
|
| 756 |
gr.update(
|
| 757 |
value="🚀 Generate Poster" if lang == "English" else "🚀 生成海报"
|
| 758 |
),
|
| 759 |
gr.update(
|
| 760 |
label="📥 Download Poster" if lang == "English" else "📥 下载海报"
|
| 761 |
),
|
| 762 |
+
gr.update(
|
| 763 |
+
label="Main Title" if lang == "English" else "主标题 (Main Title)",
|
| 764 |
+
placeholder="e.g. Shanghai or 'Our Home'" if lang == "English" else "如: Shanghai 或 '我们的家'"
|
| 765 |
+
),
|
| 766 |
+
gr.update(
|
| 767 |
+
label="Subtitle" if lang == "English" else "副标题 (Subtitle)",
|
| 768 |
+
placeholder="e.g. China or '2024.10.20'" if lang == "English" else "如: China 或 '2024.10.20'"
|
| 769 |
+
)
|
| 770 |
)
|
| 771 |
|
| 772 |
lang_radio.change(
|
|
|
|
| 776 |
country_dropdown,
|
| 777 |
province_dropdown,
|
| 778 |
city_dropdown,
|
| 779 |
+
district_dropdown,
|
| 780 |
theme_dropdown,
|
| 781 |
layers_checkbox,
|
| 782 |
],
|
|
|
|
| 796 |
show_text_checkbox,
|
| 797 |
generate_btn,
|
| 798 |
download_btn,
|
| 799 |
+
custom_city_name,
|
| 800 |
+
custom_country_name,
|
| 801 |
],
|
| 802 |
)
|
| 803 |
|
cities_data.py
CHANGED
|
@@ -1044,19 +1044,25 @@ def get_manual_coordinates(name, parent_adcode=None):
|
|
| 1044 |
|
| 1045 |
def get_china_adcode(name, parent_adcode=None):
|
| 1046 |
"""Search for adcode by name in local China data."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1047 |
info = _load_china_info()
|
| 1048 |
if not parent_adcode:
|
| 1049 |
# Check provinces
|
| 1050 |
china_entry = info.get("100000")
|
| 1051 |
if china_entry:
|
| 1052 |
for p in china_entry.get("children", []):
|
| 1053 |
-
if p["name"] ==
|
| 1054 |
return p["adcode"]
|
| 1055 |
else:
|
| 1056 |
parent_entry = info.get(str(parent_adcode))
|
| 1057 |
if parent_entry:
|
| 1058 |
for child in parent_entry.get("children", []):
|
| 1059 |
-
if child["name"] ==
|
| 1060 |
return child["adcode"]
|
| 1061 |
return None
|
| 1062 |
|
|
@@ -1209,7 +1215,7 @@ def get_cities(country_name, province_name, lang="en"):
|
|
| 1209 |
def get_districts(country_name, province_name, city_name, lang="en"):
|
| 1210 |
"""Return list of (Display, Key) tuples for districts."""
|
| 1211 |
country_key = get_country_key(country_name)
|
| 1212 |
-
if country_key != "中国":
|
| 1213 |
return []
|
| 1214 |
|
| 1215 |
p_adcode = get_china_adcode(province_name)
|
|
|
|
| 1044 |
|
| 1045 |
def get_china_adcode(name, parent_adcode=None):
|
| 1046 |
"""Search for adcode by name in local China data."""
|
| 1047 |
+
if not name:
|
| 1048 |
+
return None
|
| 1049 |
+
|
| 1050 |
+
# Strip convention-based suffixes
|
| 1051 |
+
search_name = name.replace("_WHOLE", "")
|
| 1052 |
+
|
| 1053 |
info = _load_china_info()
|
| 1054 |
if not parent_adcode:
|
| 1055 |
# Check provinces
|
| 1056 |
china_entry = info.get("100000")
|
| 1057 |
if china_entry:
|
| 1058 |
for p in china_entry.get("children", []):
|
| 1059 |
+
if p["name"] == search_name or p["name"].rstrip("省").rstrip("市") == search_name:
|
| 1060 |
return p["adcode"]
|
| 1061 |
else:
|
| 1062 |
parent_entry = info.get(str(parent_adcode))
|
| 1063 |
if parent_entry:
|
| 1064 |
for child in parent_entry.get("children", []):
|
| 1065 |
+
if child["name"] == search_name:
|
| 1066 |
return child["adcode"]
|
| 1067 |
return None
|
| 1068 |
|
|
|
|
| 1215 |
def get_districts(country_name, province_name, city_name, lang="en"):
|
| 1216 |
"""Return list of (Display, Key) tuples for districts."""
|
| 1217 |
country_key = get_country_key(country_name)
|
| 1218 |
+
if country_key != "中国" or not city_name or city_name.endswith("_WHOLE"):
|
| 1219 |
return []
|
| 1220 |
|
| 1221 |
p_adcode = get_china_adcode(province_name)
|
create_map_poster.py
CHANGED
|
@@ -552,28 +552,44 @@ def create_poster(
|
|
| 552 |
|
| 553 |
# --- BOTTOM TEXT ---
|
| 554 |
if show_text:
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
|
|
|
|
|
|
| 565 |
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 576 |
|
|
|
|
| 577 |
lat, lon = point
|
| 578 |
coords_text = (
|
| 579 |
f"{lat:.4f}° N / {lon:.4f}° E"
|
|
@@ -595,15 +611,6 @@ def create_poster(
|
|
| 595 |
zorder=11,
|
| 596 |
)
|
| 597 |
|
| 598 |
-
ax.plot(
|
| 599 |
-
[0.4, 0.6],
|
| 600 |
-
[0.125, 0.125],
|
| 601 |
-
transform=ax.transAxes,
|
| 602 |
-
color=THEME["text"],
|
| 603 |
-
linewidth=1,
|
| 604 |
-
zorder=11,
|
| 605 |
-
)
|
| 606 |
-
|
| 607 |
# --- ATTRIBUTION (bottom right) ---
|
| 608 |
attr_font = font_sub_base.copy()
|
| 609 |
attr_font.set_size(8)
|
|
|
|
| 552 |
|
| 553 |
# --- BOTTOM TEXT ---
|
| 554 |
if show_text:
|
| 555 |
+
# Title
|
| 556 |
+
if display_city:
|
| 557 |
+
ax.text(
|
| 558 |
+
0.5,
|
| 559 |
+
0.14,
|
| 560 |
+
display_city,
|
| 561 |
+
transform=ax.transAxes,
|
| 562 |
+
color=THEME["text"],
|
| 563 |
+
ha="center",
|
| 564 |
+
fontproperties=font_main,
|
| 565 |
+
zorder=11,
|
| 566 |
+
)
|
| 567 |
|
| 568 |
+
# Subtitle
|
| 569 |
+
if display_country:
|
| 570 |
+
ax.text(
|
| 571 |
+
0.5,
|
| 572 |
+
0.10,
|
| 573 |
+
display_country,
|
| 574 |
+
transform=ax.transAxes,
|
| 575 |
+
color=THEME["text"],
|
| 576 |
+
ha="center",
|
| 577 |
+
fontproperties=font_sub,
|
| 578 |
+
zorder=11,
|
| 579 |
+
)
|
| 580 |
+
|
| 581 |
+
# Decorative separator line (only show if both exist)
|
| 582 |
+
if display_city and display_country:
|
| 583 |
+
ax.plot(
|
| 584 |
+
[0.4, 0.6],
|
| 585 |
+
[0.125, 0.125],
|
| 586 |
+
transform=ax.transAxes,
|
| 587 |
+
color=THEME["text"],
|
| 588 |
+
linewidth=1,
|
| 589 |
+
zorder=11,
|
| 590 |
+
)
|
| 591 |
|
| 592 |
+
# Coordinates (Third line, smaller)
|
| 593 |
lat, lon = point
|
| 594 |
coords_text = (
|
| 595 |
f"{lat:.4f}° N / {lon:.4f}° E"
|
|
|
|
| 611 |
zorder=11,
|
| 612 |
)
|
| 613 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 614 |
# --- ATTRIBUTION (bottom right) ---
|
| 615 |
attr_font = font_sub_base.copy()
|
| 616 |
attr_font.set_size(8)
|