Backup before syncing server scripts
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
build-sprite.py — يبني sprite sheet لـ MapLibre من مجلد SVG.
|
||||
|
||||
لماذا: MapLibre لا يقرأ ملفات SVG مفردة. يحتاج صورة PNG واحدة تُرصف فيها كل
|
||||
الآيقونات + فهرس JSON يحدد موضع وحجم كل واحدة. كان الستايل يشير إلى
|
||||
demotiles.maplibre.org (خادم تجريبي)، وحين يفشل تحميله يرسم MapLibre النص
|
||||
بلا آيقونة وبصمت — فتبدو الخريطة بأسماء بلا رموز.
|
||||
|
||||
الاستخدام:
|
||||
pip install cairosvg pillow
|
||||
python3 infrastructure/scripts/build-sprite.py
|
||||
|
||||
المخرجات في apps/web/public/:
|
||||
sprite.png / sprite.json (1x)
|
||||
sprite@2x.png / sprite@2x.json (شاشات Retina — MapLibre يطلبها تلقائياً)
|
||||
|
||||
ثم في الستايل: "sprite": "https://map-saas.intaleqapp.com/sprite"
|
||||
(بلا امتداد — MapLibre يضيف .png/.json و@2x بنفسه)
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import cairosvg
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
sys.exit("ينقص: pip install cairosvg pillow")
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
ICONS_DIR = os.path.join(ROOT, "apps", "web", "public", "icons")
|
||||
OUT_DIR = os.path.join(ROOT, "apps", "web", "public")
|
||||
|
||||
BASE_SIZE = 24 # بكسل للآيقونة عند 1x
|
||||
PADDING = 2 # فاصل يمنع نزّ ألوان الجيران عند التصغير (bleeding)
|
||||
|
||||
|
||||
def build(scale: int, suffix: str) -> None:
|
||||
size = BASE_SIZE * scale
|
||||
pad = PADDING * scale
|
||||
|
||||
svgs = sorted(f for f in os.listdir(ICONS_DIR) if f.endswith(".svg"))
|
||||
if not svgs:
|
||||
sys.exit(f"لا توجد ملفات SVG في {ICONS_DIR}")
|
||||
|
||||
rendered = []
|
||||
for fname in svgs:
|
||||
name = os.path.splitext(fname)[0]
|
||||
png_bytes = cairosvg.svg2png(
|
||||
url=os.path.join(ICONS_DIR, fname),
|
||||
output_width=size,
|
||||
output_height=size,
|
||||
)
|
||||
import io
|
||||
rendered.append((name, Image.open(io.BytesIO(png_bytes)).convert("RGBA")))
|
||||
|
||||
# شبكة مربعة تقريباً: أقل هدراً من صف واحد طويل
|
||||
cols = max(1, int(len(rendered) ** 0.5 + 0.999))
|
||||
rows = (len(rendered) + cols - 1) // cols
|
||||
|
||||
sheet = Image.new(
|
||||
"RGBA",
|
||||
(cols * (size + pad) - pad, rows * (size + pad) - pad),
|
||||
(0, 0, 0, 0),
|
||||
)
|
||||
|
||||
index = {}
|
||||
for i, (name, img) in enumerate(rendered):
|
||||
x = (i % cols) * (size + pad)
|
||||
y = (i // cols) * (size + pad)
|
||||
sheet.paste(img, (x, y))
|
||||
index[name] = {
|
||||
"x": x, "y": y,
|
||||
"width": size, "height": size,
|
||||
"pixelRatio": scale,
|
||||
"sdf": False,
|
||||
}
|
||||
|
||||
sheet.save(os.path.join(OUT_DIR, f"sprite{suffix}.png"))
|
||||
with open(os.path.join(OUT_DIR, f"sprite{suffix}.json"), "w") as fh:
|
||||
json.dump(index, fh, indent=2)
|
||||
|
||||
print(f"✅ sprite{suffix}: {len(index)} آيقونة، {sheet.width}×{sheet.height}px")
|
||||
return list(index)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
names = build(1, "")
|
||||
build(2, "@2x")
|
||||
print("\nالآيقونات المتاحة لـ icon-image:")
|
||||
print(" " + ", ".join(names))
|
||||
print("\n⚠️ طبقات الستايل تطلب أسماء قد لا تكون موجودة أعلاه")
|
||||
print(" (college, rail, tourist ...) — راجعها أو أضف SVG مقابلاً لكل ناقص.")
|
||||
Reference in New Issue
Block a user