Penanda SVG di Matplotlib: Periksa Jalurnya
Gunakan jalur SVG sederhana sebagai penanda diagram pencar Matplotlib. Ulangi pemeriksaan pusat, orientasi, lubang, dan ukuran pada ekspor SVG serta PDF asli.
Di halaman ini
Untuk menggunakan SVG sebagai penanda khusus diagram pencar Matplotlib, ubah geometri yang didukung menjadi matplotlib.path.Path, pusatkan pada (0, 0), lalu berikan objek itu ke scatter(marker=...). Warna dan ukuran penanda kemudian ditentukan oleh plot. API penanda Matplotlib menerima Path; API itu tidak menerima dokumen SVG sembarang sebagai nama berkas penanda.
Tutorial ini menggunakan poligon asimetris asli dengan lubang persegi panjang. Skrip lengkap di bawah menangani subset SVG yang sengaja kecil dan menolak fitur yang tidak dapat dipertahankannya. Kami menjalankannya dengan Matplotlib 3.10.7, memeriksa jalur SVG dan PDF tersimpan, serta memeriksa kedua hasil render.
Jika Anda menginginkan satu ilustrasi di samping grafik yang diekspor, gunakan panduan terpisah komposisi gambar SVG Matplotlib. Penanda diagram pencar berulang pada koordinat data, sehingga pusat, orientasi, dan ukurannya memerlukan pemeriksaan berbeda.
1. Mulai dengan satu bentuk yang didukung
Anda memerlukan Python dengan Matplotlib dan NumPy serta izin menulis direktori keluaran. Contoh ini menyertakan SVG aslinya, jadi Anda tidak perlu mengunduh ikon. Tiga koordinat yang diplot adalah nilai ilustratif, bukan pengamatan penelitian.
Pengurai menerima tepat satu path berisian hitam di dalam akar SVG dengan viewBox. Setiap kontur harus ditutup dengan Z; setiap pasangan koordinat memerlukan perintah huruf besar M atau L yang eksplisit. Pengurai mendukung jalur majemuk dengan kontur luar dan lubang berarah berlawanan menggunakan fill-rule="nonzero".
Pengurai menolak kurva, perintah relatif, transformasi, goresan, CSS, beberapa elemen path, teks, pemotongan, gradien, dan warna lain. Atribut akar tambahan seperti width dan height juga ditolak. Pembatasan ini membuat contoh mudah diperiksa; ini bukan pengimpor SVG umum. Simpan salinan gambar yang belum diubah sebelum menyiapkan masukan yang kompatibel.
Path Matplotlib menyimpan titik sudut dan kode penggambaran, bukan seluruh model pewarnaan SVG. API Path menetapkan operasi pindah, garis, dan tutup yang digunakan di sini. Jika simbol membutuhkan beberapa warna atau efek, pilih alur yang mempertahankannya daripada diam-diam membuangnya agar cocok dengan pengurai ini.
2. Jalankan contoh penanda lengkap
Simpan sebagai scatter_marker.py, lalu jalankan python scatter_marker.py dalam direktori yang sama. Skrip membuat marker-output/marker.svg, scatter.svg, scatter.pdf, dan pratinjau PNG. Sumber SVG yang dihasilkan serta semua pengaturan plot disertakan di sini.
from pathlib import Path as FilePath
import re
import xml.etree.ElementTree as ET
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.path import Path
import numpy as np
SVG = '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 80">
<path fill="#000000" fill-rule="nonzero"
d="M10 10 L70 10 L70 30 L90 30 L90 70 L10 70 Z
M25 25 L25 55 L45 55 L45 25 Z"/>
</svg>'''
def svg_marker(svg_text):
"""One black, nonzero-filled path; explicit absolute M/L/Z only."""
namespace = "{http://www.w3.org/2000/svg}"
root = ET.fromstring(svg_text)
if root.tag != namespace + "svg" or set(root.attrib) != {"viewBox"}:
raise ValueError("Use only an SVG root with a viewBox")
view = np.array([float(value) for value in root.attrib["viewBox"].split()])
if view.shape != (4,) or not np.isfinite(view).all() or (view[2:] <= 0).any():
raise ValueError("Invalid viewBox")
if len(root) != 1 or root[0].tag != namespace + "path":
raise ValueError("Use exactly one path, with no groups or other elements")
node = root[0]
if any((text or "").strip() for text in [root.text, node.text, node.tail]):
raise ValueError("Text content is not supported")
if set(node.attrib) - {"d", "fill", "fill-rule"} or len(node):
raise ValueError("Unsupported path attribute or child element")
if node.get("fill", "black") not in {"black", "#000", "#000000"}:
raise ValueError("Use one solid black fill")
if node.get("fill-rule", "nonzero") != "nonzero":
raise ValueError("Use nonzero winding; evenodd is not converted here")
data = node.get("d", "")
pattern = r"[MLZ]|[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?"
tokens = re.findall(pattern, data)
if re.sub(pattern, "", data).strip(" ,\n\r\t"):
raise ValueError("Only explicit absolute M, L and Z commands are supported")
vertices, codes = [], []
index, start, open_contour, lines = 0, None, False, 0
while index < len(tokens):
command = tokens[index]
if command in {"M", "L"}:
if index + 2 >= len(tokens) or tokens[index + 1] in {"M", "L", "Z"} or tokens[index + 2] in {"M", "L", "Z"}:
raise ValueError("Each M/L needs one explicit coordinate pair")
point = tuple(float(value) for value in tokens[index + 1:index + 3])
if not np.isfinite(point).all():
raise ValueError("Coordinates must be finite")
if command == "M":
if open_contour:
raise ValueError("Close each contour with Z")
start, open_contour, lines = point, True, 0
codes.append(Path.MOVETO)
else:
if not open_contour:
raise ValueError("Start each contour with M")
lines += 1
codes.append(Path.LINETO)
vertices.append(point)
index += 3
elif command == "Z":
if not open_contour or lines < 2:
raise ValueError("A closed contour needs at least three vertices")
vertices.append(start)
codes.append(Path.CLOSEPOLY)
open_contour = False
index += 1
else:
raise ValueError("Repeat M/L explicitly before coordinate pairs")
if open_contour or not vertices:
raise ValueError("Supply nonempty closed contours")
path = Path(np.array(vertices), codes)
box = path.get_extents()
if box.width <= 0 or box.height <= 0:
raise ValueError("Path needs nonzero width and height")
if box.x0 < view[0] or box.y0 < view[1] or box.x1 > view[0] + view[2] or box.y1 > view[1] + view[3]:
raise ValueError("Path outside viewBox: resolve clipping before import")
center = np.array([(box.x0 + box.x1) / 2, (box.y0 + box.y1) / 2])
centered = (path.vertices - center) * np.array([1, -1])
return Path(centered, path.codes)
if __name__ == "__main__":
output = FilePath("marker-output")
output.mkdir(exist_ok=True)
(output / "marker.svg").write_text(SVG)
marker = svg_marker(SVG)
plt.rcParams.update({"svg.fonttype": "none", "pdf.fonttype": 42,
"font.family": "DejaVu Sans", "font.size": 11})
fig, ax = plt.subplots(figsize=(8, 3), dpi=144)
fig.subplots_adjust(left=.1, right=.96, bottom=.24, top=.8)
ax.scatter([1, 2, 3], [1.3, 2.4, 1.6], s=[400, 1600, 3600],
marker=marker, color="#2f6977", linewidths=0, edgecolors="none")
ax.set(xlim=(0, 4), ylim=(0, 4), xlabel="Illustrative x", ylabel="Illustrative y",
title="Original SVG marker: s = 400, 1600, 3600 points squared")
ax.spines[["top", "right"]].set_visible(False)
fig.savefig(output / "scatter.svg", format="svg")
fig.savefig(output / "scatter.pdf", format="pdf")
fig.savefig(output / "scatter.png", format="png")
plt.close(fig)Buka scatter.svg dan scatter.pdf. Anda seharusnya melihat tiga versi hijau kebiruan dari bentuk yang sama: lubang persegi panjang di kiri dan bagian bertingkat yang menonjol ke kanan bawah. Bandingkan penanda terkecil pada ukuran tampilan yang dituju, bukan hanya saat diperbesar.

3. Periksa pusat dan balik Y satu kali
Gambar sumber menempati x=10…90 dan y=10…70. Pusat kotak pembatasnya adalah (50, 40). Mengurangi koordinat pusat menghasilkan batas −40…40 secara horizontal dan −30…30 secara vertikal. Ini pemusatan berdasarkan kotak pembatas, bukan sentroid atau perkiraan keseimbangan visual.
Bagian bertingkat kanan bawah berguna karena lingkaran simetris akan menyembunyikan pembalikan vertikal. Dalam sistem koordinat SVG awal, Y positif mengarah ke bawah. Skrip menegatifkan Y sekali untuk mempertahankan orientasi tampilan sumber dalam plot normal dengan Y meningkat ke atas.
Kedua kontur menerima transformasi yang sama. Arah relatifnya tetap berlawanan, sehingga bukaan persegi panjang tetap menjadi lubang. Aturan isian nonzero SVG bergantung pada arah kontur; sumber evenodd secara eksplisit ditolak di sini, bukan dianggap setara.
Matplotlib lalu menormalisasi Path yang telah dipusatkan ini. Pada versi yang diuji, hasil membentang −0.5…0.5 pada X dan −0.375…0.375 pada Y. Titik yang diberikan ke scatter berada di pusat batas tersebut. Anda dapat sengaja memilih jangkar lain untuk simbol khusus, tetapi jangan menganggap sudut kiri atas SVG sumber sebagai jangkar data yang sesuai.
4. Baca ukuran diagram pencar dalam poin kuadrat
API scatter mendefinisikan s dalam poin kuadrat, dengan satu poin tipografi sama dengan 1/72 inci. Tepi penanda yang tidak nol dapat memperbesar jangkauan tampilannya. Contoh menetapkan linewidths=0 dan edgecolors="none" agar pemeriksaan ukuran hanya menyangkut geometri berisian.
Untuk penanda yang dinormalisasi ini, lebarnya satu satuan penanda dan tingginya 0,75 satuan. Karena itu, lebar tersimpannya adalah sqrt(s) poin dan tingginya 0.75 * sqrt(s) poin.
s (poin kuadrat) | Lebar yang diharapkan dan diukur | Tinggi yang diharapkan dan diukur |
|---|---|---|
| 400 | 20 pt | 15 pt |
| 1600 | 40 pt | 30 pt |
| 3600 | 60 pt | 45 pt |
Ini adalah dimensi bentuk normalisasi tertentu ini, bukan luas yang diwarnai. Rasio aspek, jangkar, atau goresan tepi yang berbeda mengubah hubungan antara parameter ukuran dan simbol terlihat. Memperbesar s empat kali menggandakan dimensinya dalam contoh ini.
Kami memeriksa koordinat SVG yang benar-benar disimpan serta operasi jalur berisi PDF dengan toleransi numerik 0,000001 poin. Ketiga penanda memiliki batas yang diharapkan, penempatan terpusat pada data, dan titik sudut yang ditransformasi. SVG tersimpan tidak mengandung elemen gambar, dan PDF tidak mengandung objek gambar.
Kami juga merender PDF tersimpan pada 144 dpi dan memeriksa tiga posisi interior yang ditetapkan: lubang putih, takik kanan atas putih, dan tonjolan kanan bawah hijau kebiruan. Render asli SVG sebenarnya di browser lulus pemeriksaan interior yang sama. Ini memverifikasi orientasi dan bukaan terpilih; bukan mengklaim tepi antialias identik antarrenderer atau dukungan untuk gambar SVG sembarang.
5. Perbaiki penyebab sebelum menyesuaikan ukuran
| Gejala | Pemeriksaan dan pemulihan |
|---|---|
| Pengurai memunculkan galat fitur tidak didukung | Periksa fitur yang disebutkan. Siapkan masukan poligon kompatibel secara terpisah, atau gunakan metode konversi yang lebih lengkap dan uji hasilnya. Menghapus atribut tanpa mempertahankan efeknya dapat mengubah gambar. |
| Bentuk terbalik | Periksa apakah Y sudah dibalik sebelum fungsi ini. Terapkan perubahan koordinat sekali, lalu bandingkan ciri asimetris. |
| Simbol berada jauh dari titik data | Periksa batas dan jangkar yang dipilih. Fungsi ini memusatkan batas gambar terlepas dari ruang kosong dalam viewBox. |
| Lubang terisi | Periksa arah kontur dan aturan isian. Pertahankan arah berlawanan untuk bentuk uji berisian nonzero ini; pengurai tidak mengonversi geometri evenodd. |
| Berkas tersimpan mengandung simbol raster | Periksa elemen atau sumber daya hasil ekspor. Nama berkas SVG atau PDF saja tidak membuktikan setiap elemen plot berbentuk vektor. |
| Penanda kecil kehilangan detail berguna | Tinjau gambar pada ukuran akhirnya. Perbesar penanda, sederhanakan simbol, atau gunakan penanda bawaan jika lebih jelas menyampaikan kategori. |
Untuk pemeriksaan berkas di luar bentuk uji ini, lihat cara menemukan konten raster dalam SVG. Untuk keseluruhan gambar, tinjau ukuran font dan ketebalan garis pada ukuran publikasi; jalur penanda yang benar pun dapat terlalu rumit untuk legenda kecil atau panel padat.
Sebelum menggunakan penanda sendiri pada gambar akhir, periksa ukuran terkecil, tampilan legenda, titik bertumpuk, serta SVG/PDF hasil ekspor. Pertahankan data, sumbu, label, dan pemetaan skala dalam alur pembuatan plot Anda. Ikon khusus tidak menggantikan definisi numerik tersebut.
Saat gambar raster memerlukan jalur baru
Jika simbol hanya tersisa sebagai gambar raster datar, alur diagram ilmiah ke SVG dari PerfectVector dapat membantu memulihkan gambar yang dapat diedit. Bandingkan kontur yang dipulihkan dan setiap bukaan dengan aslinya sebelum menyesuaikannya ke alur penanda. Pertahankan SVG bersih yang sudah ada sebagai sumber bila tersedia.
Pemulihan vektor tidak merekonstruksi data dasar grafik atau menjamin kompatibilitas dengan pengurai terbatas ini. Pertahankan foto dan ilustrasi bernada kontinu ketika tampilan rasternya merupakan bagian dari gambar, lalu pilih metode penempatan berbasis gambar yang sesuai untuk konten itu.
Tanya jawab
Bisakah nama berkas SVG langsung diberikan ke scatter Matplotlib? Tidak. API penanda menerima deskripsi penanda yang didukung, termasuk Path Matplotlib. Contoh ini mengonversi jalur SVG terbatas menjadi objek tersebut sebelum memanggil scatter.
Mengapa penanda khusus saya terbalik? SVG sumber dan plot mungkin menggunakan arah Y berbeda. Contoh ini menegatifkan Y SVG sekali; periksa ciri asimetris agar pembalikan tidak diterapkan dua kali.
Apakah s sama dengan luas yang diwarnai pada bentuk SVG saya? Jangan menggunakannya sebagai nilai luas berwarna hasil pengukuran. Scatter mendefinisikan s dalam poin kuadrat, sementara bentuk yang dinormalisasi dan goresan tepi menentukan dimensi terlihatnya. Bentuk uji ini memiliki lebar 20, 40, dan 60 poin pada nilai s 400, 1600, dan 3600.
Apakah kode ini mempertahankan semua fitur SVG? Tidak. Kode mendukung satu jalur hitam berisian nonzero dengan perintah absolut eksplisit M, L, Z dan kontur tertutup. Pewarnaan, perintah geometri, transformasi, dan elemen tambahan yang tidak didukung memunculkan galat.
Sumber
- API scatter Matplotlib — Mendefinisikan parameter ukuran, masukan penanda, dan interaksi ketebalan tepi.
- API penanda Matplotlib 3.10.7 — Mendokumentasikan penanda Path dan koordinat penanda yang dinormalisasi untuk rilis yang diuji.
- API Path Matplotlib — Mendefinisikan titik sudut, kode penggambaran, dan kontur tertutup.
- Sistem koordinat SVG — Menetapkan arah koordinat awal yang digunakan dalam pemeriksaan orientasi.
- Aturan isian SVG — Menjelaskan pengaruh arah kontur terhadap bukaan berisian nonzero.
Untuk simbol yang hanya tersedia sebagai raster, pulihkan gambar diagram yang dapat diedit, bandingkan kontur dan lubangnya dengan sumber, lalu uji jalur yang disesuaikan dalam gambar sebenarnya sebelum penyerahan.
Lebih banyak dari blog

SVG dalam LaTeX: Pertahankan Bentuk dan Atur Tipografi Label Gambar
Pilih alur kerja PDF atau PDF plus LaTeX untuk ilustrasi SVG Anda. Jaga label dapat diedit, pahami dependensi Inkscape, dan periksa PDF dokumen akhir.

Abaikan Area SVG Transparan dalam Deteksi Klik Konva
Buat area SVG transparan mengabaikan klik di Konva. Coba deteksi hit berbasis cache, uji lubang dan latar, serta bangun ulang hit setelah sumber berubah.
