Corriger le sens de la flèche du nord, éviter les débordements de texte et hachurer les mailles sans donnée de la planche PDF
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -324,12 +324,66 @@ def _hex(rgb):
|
|||||||
|
|
||||||
|
|
||||||
def _convergence_deg(cx, cy):
|
def _convergence_deg(cx, cy):
|
||||||
"""Angle (°) du nord géographique par rapport au nord du quadrillage L93."""
|
"""Convergence du méridien : azimut (°) du nord du quadrillage L93 mesuré
|
||||||
|
depuis le nord géographique (sens horaire), positif à l'est du méridien
|
||||||
|
central (3°E)."""
|
||||||
lat0, lon0 = _to_wgs84(cx, cy)
|
lat0, lon0 = _to_wgs84(cx, cy)
|
||||||
lat1, lon1 = _to_wgs84(cx, cy + 100.0)
|
lat1, lon1 = _to_wgs84(cx, cy + 100.0)
|
||||||
return math.degrees(math.atan2((lon1 - lon0) * math.cos(math.radians(lat0)), lat1 - lat0))
|
return math.degrees(math.atan2((lon1 - lon0) * math.cos(math.radians(lat0)), lat1 - lat0))
|
||||||
|
|
||||||
|
|
||||||
|
def _north_arrow_angle(cx, cy):
|
||||||
|
"""Angle de rotation reportlab (sens antihoraire, `Canvas.rotate`) pour que
|
||||||
|
la flèche du nord — dessinée vers le haut, c'est-à-dire vers le nord du
|
||||||
|
quadrillage — pointe vers le nord géographique."""
|
||||||
|
return _convergence_deg(cx, cy)
|
||||||
|
|
||||||
|
|
||||||
|
def _wrap_text(c, text, font, size, max_width, sep=" "):
|
||||||
|
"""Découpe `text` sur `sep` en lignes tenant chacune dans `max_width` (pt)."""
|
||||||
|
parts = text.split(sep)
|
||||||
|
lines, cur = [], parts[0]
|
||||||
|
for part in parts[1:]:
|
||||||
|
candidate = cur + sep + part
|
||||||
|
if c.stringWidth(candidate, font, size) <= max_width:
|
||||||
|
cur = candidate
|
||||||
|
else:
|
||||||
|
lines.append(cur)
|
||||||
|
cur = part
|
||||||
|
lines.append(cur)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def _fit_title(c, text, font, size, max_width, min_size=7.0):
|
||||||
|
"""Réduit la taille de police jusqu'à `min_size` puis tronque avec des
|
||||||
|
points de suspension pour tenir `text` dans `max_width` (pt)."""
|
||||||
|
while size > min_size and c.stringWidth(text, font, size) > max_width:
|
||||||
|
size -= 0.5
|
||||||
|
if c.stringWidth(text, font, size) > max_width:
|
||||||
|
while text and c.stringWidth(text + "...", font, size) > max_width:
|
||||||
|
text = text[:-1]
|
||||||
|
text = text.rstrip() + "..."
|
||||||
|
return text, size
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_hatch(c, x, y, w, h, mm, step=3.0):
|
||||||
|
"""Hachures grises sur fond blanc (maille sans donnée), comme sur la carte."""
|
||||||
|
from reportlab.lib.colors import Color, white
|
||||||
|
if w <= 0 or h <= 0:
|
||||||
|
return
|
||||||
|
c.saveState()
|
||||||
|
p = c.beginPath(); p.rect(x * mm, y * mm, w * mm, h * mm)
|
||||||
|
c.clipPath(p, stroke=0, fill=0)
|
||||||
|
c.setFillColor(white)
|
||||||
|
c.rect(x * mm, y * mm, w * mm, h * mm, stroke=0, fill=1)
|
||||||
|
c.setStrokeColor(Color(0.75, 0.75, 0.75)); c.setLineWidth(0.3)
|
||||||
|
n = int((w + h) / step) + 2
|
||||||
|
for k in range(-n, n):
|
||||||
|
x0 = x + k * step
|
||||||
|
c.line(x0 * mm, y * mm, (x0 + h) * mm, (y + h) * mm)
|
||||||
|
c.restoreState()
|
||||||
|
|
||||||
|
|
||||||
def _panel_boxes(lay):
|
def _panel_boxes(lay):
|
||||||
"""Rectangles (x, y, w, h) mm du bandeau : légende, qualité, cartouche."""
|
"""Rectangles (x, y, w, h) mm du bandeau : légende, qualité, cartouche."""
|
||||||
gap = 4.0
|
gap = 4.0
|
||||||
@ -379,7 +433,8 @@ def _draw_grid(c, lay, bbox, mm):
|
|||||||
((bbox[0], bbox[1]), (lay.map_x, lay.map_y - 6.2, "l")),
|
((bbox[0], bbox[1]), (lay.map_x, lay.map_y - 6.2, "l")),
|
||||||
((bbox[2], bbox[1]), (lay.map_x + lay.map_w, lay.map_y - 6.2, "r"))):
|
((bbox[2], bbox[1]), (lay.map_x + lay.map_w, lay.map_y - 6.2, "r"))):
|
||||||
lat, lon = _to_wgs84(gx, gy)
|
lat, lon = _to_wgs84(gx, gy)
|
||||||
txt = f"{lat:.5f} N {lon:.5f} E"
|
hemi = "E" if lon >= 0 else "O"
|
||||||
|
txt = f"{lat:.5f} N {abs(lon):.5f} {hemi}"
|
||||||
(c.drawString if align == "l" else c.drawRightString)(px * mm, py * mm, txt)
|
(c.drawString if align == "l" else c.drawRightString)(px * mm, py * mm, txt)
|
||||||
c.setFont("Helvetica", 5.5)
|
c.setFont("Helvetica", 5.5)
|
||||||
c.drawString(lay.map_x * mm, (lay.map_y + lay.map_h + 6.2) * mm,
|
c.drawString(lay.map_x * mm, (lay.map_y + lay.map_h + 6.2) * mm,
|
||||||
@ -414,11 +469,12 @@ def _draw_legend(c, box, mm):
|
|||||||
start, 5.2, stroke=0, fill=1)
|
start, 5.2, stroke=0, fill=1)
|
||||||
c.setFillColor(white)
|
c.setFillColor(white)
|
||||||
c.circle(rcx * mm, rcy * mm, r_in * mm, stroke=0, fill=1)
|
c.circle(rcx * mm, rcy * mm, r_in * mm, stroke=0, fill=1)
|
||||||
c.setFillColor(black); c.setFont("Helvetica-Bold", 5.5)
|
c.setFillColor(black); c.setFont("Helvetica-Bold", 5.0)
|
||||||
for label, deg in (("N", 0), ("E", 90), ("S", 180), ("O", 270)):
|
for label, deg in (("N", 0), ("NE", 45), ("E", 90), ("SE", 135), ("S", 180),
|
||||||
|
("SO", 225), ("O", 270), ("NO", 315)):
|
||||||
a = math.radians(deg)
|
a = math.radians(deg)
|
||||||
c.drawCentredString((rcx + (r_out + 2) * math.sin(a)) * mm,
|
c.drawCentredString((rcx + (r_out + 2.4) * math.sin(a)) * mm,
|
||||||
(rcy + (r_out + 2) * math.cos(a) - 0.8) * mm, label)
|
(rcy + (r_out + 2.4) * math.cos(a) - 0.8) * mm, label)
|
||||||
c.setFont("Helvetica", 6)
|
c.setFont("Helvetica", 6)
|
||||||
c.drawString((rcx + r_out + 5) * mm, (rcy + 2) * mm, _pdf_text("Teinte = orientation"))
|
c.drawString((rcx + r_out + 5) * mm, (rcy + 2) * mm, _pdf_text("Teinte = orientation"))
|
||||||
c.drawString((rcx + r_out + 5) * mm, (rcy - 1) * mm, _pdf_text("de la pente"))
|
c.drawString((rcx + r_out + 5) * mm, (rcy - 1) * mm, _pdf_text("de la pente"))
|
||||||
@ -435,7 +491,7 @@ def _draw_legend(c, box, mm):
|
|||||||
|
|
||||||
def _draw_quality(c, box, bbox, zq, mm):
|
def _draw_quality(c, box, bbox, zq, mm):
|
||||||
"""Encart qualité : miniature de densité sol + chiffres clés."""
|
"""Encart qualité : miniature de densité sol + chiffres clés."""
|
||||||
from reportlab.lib.colors import HexColor, black, Color
|
from reportlab.lib.colors import HexColor, black, white
|
||||||
x, y, w, h = box
|
x, y, w, h = box
|
||||||
c.setFillColor(black); c.setFont("Helvetica-Bold", 8)
|
c.setFillColor(black); c.setFont("Helvetica-Bold", 8)
|
||||||
c.drawString(x * mm, (y + h - 4) * mm, _pdf_text("Qualité des données"))
|
c.drawString(x * mm, (y + h - 4) * mm, _pdf_text("Qualité des données"))
|
||||||
@ -444,8 +500,7 @@ def _draw_quality(c, box, bbox, zq, mm):
|
|||||||
k = min(avail_w / (bbox[2] - bbox[0]), avail_h / (bbox[3] - bbox[1]))
|
k = min(avail_w / (bbox[2] - bbox[0]), avail_h / (bbox[3] - bbox[1]))
|
||||||
mw, mh = (bbox[2] - bbox[0]) * k, (bbox[3] - bbox[1]) * k
|
mw, mh = (bbox[2] - bbox[0]) * k, (bbox[3] - bbox[1]) * k
|
||||||
mx, my = x, y + h - 7 - mh
|
mx, my = x, y + h - 7 - mh
|
||||||
c.setFillColor(Color(0.85, 0.85, 0.85))
|
_draw_hatch(c, mx, my, mw, mh, mm) # hachures = non renseigné
|
||||||
c.rect(mx * mm, my * mm, mw * mm, mh * mm, stroke=0, fill=1) # gris = non renseigné
|
|
||||||
for gx0, gy0, gx1, gy1, v in zq["grid_cells"]:
|
for gx0, gy0, gx1, gy1, v in zq["grid_cells"]:
|
||||||
x0, x1 = max(gx0, bbox[0]), min(gx1, bbox[2])
|
x0, x1 = max(gx0, bbox[0]), min(gx1, bbox[2])
|
||||||
y0, y1 = max(gy0, bbox[1]), min(gy1, bbox[3])
|
y0, y1 = max(gy0, bbox[1]), min(gy1, bbox[3])
|
||||||
@ -456,14 +511,17 @@ def _draw_quality(c, box, bbox, zq, mm):
|
|||||||
c.rect(mx * mm, my * mm, mw * mm, mh * mm, stroke=1, fill=0)
|
c.rect(mx * mm, my * mm, mw * mm, mh * mm, stroke=1, fill=0)
|
||||||
# Classes
|
# Classes
|
||||||
lx, ly = x + mw + 3, y + h - 8
|
lx, ly = x + mw + 3, y + h - 8
|
||||||
c.setFont("Helvetica", 5.8)
|
c.setFillColor(black); c.setFont("Helvetica", 5.8)
|
||||||
c.drawString(lx * mm, ly * mm, _pdf_text("Points sol / m²"))
|
c.drawString(lx * mm, ly * mm, _pdf_text("Points sol / m²"))
|
||||||
for low, label, color in DENSITY_CLASSES:
|
for low, label, color in DENSITY_CLASSES:
|
||||||
ly -= 3.2
|
ly -= 3.2
|
||||||
c.setFillColor(HexColor(color)); c.rect(lx * mm, ly * mm, 3 * mm, 2.2 * mm, stroke=0, fill=1)
|
c.setFillColor(HexColor(color)); c.rect(lx * mm, ly * mm, 3 * mm, 2.2 * mm, stroke=0, fill=1)
|
||||||
c.setFillColor(black); c.drawString((lx + 4) * mm, (ly + 0.4) * mm, _pdf_text(label))
|
c.setFillColor(black); c.drawString((lx + 4) * mm, (ly + 0.4) * mm, _pdf_text(label))
|
||||||
ly -= 3.2
|
ly -= 3.2
|
||||||
c.setFillColor(Color(0.85, 0.85, 0.85)); c.rect(lx * mm, ly * mm, 3 * mm, 2.2 * mm, stroke=0, fill=1)
|
c.setFillColor(white); c.rect(lx * mm, ly * mm, 3 * mm, 2.2 * mm, stroke=0, fill=1)
|
||||||
|
_draw_hatch(c, lx, ly, 3.0, 2.2, mm, step=1.4)
|
||||||
|
c.setStrokeColor(black); c.setLineWidth(0.3)
|
||||||
|
c.rect(lx * mm, ly * mm, 3 * mm, 2.2 * mm, stroke=1, fill=0)
|
||||||
c.setFillColor(black); c.drawString((lx + 4) * mm, (ly + 0.4) * mm, _pdf_text("non renseigné"))
|
c.setFillColor(black); c.drawString((lx + 4) * mm, (ly + 0.4) * mm, _pdf_text("non renseigné"))
|
||||||
# Chiffres clés
|
# Chiffres clés
|
||||||
lines = []
|
lines = []
|
||||||
@ -481,16 +539,21 @@ def _draw_quality(c, box, bbox, zq, mm):
|
|||||||
label = "Acquisition" if zq["acq_sources"] == {"gps"} else "Date de production du fichier"
|
label = "Acquisition" if zq["acq_sources"] == {"gps"} else "Date de production du fichier"
|
||||||
lines.append(f"{label} : {period}")
|
lines.append(f"{label} : {period}")
|
||||||
if zq["missing_relief"]:
|
if zq["missing_relief"]:
|
||||||
lines.append("Sans relief : " + ", ".join(f"{c_}_{r_}" for c_, r_ in zq["missing_relief"]))
|
lines.append("Donnée manquante (sans relief) : " + ", ".join(
|
||||||
|
f"{c_}_{r_}" for c_, r_ in zq["missing_relief"]))
|
||||||
if zq["missing_quality"]:
|
if zq["missing_quality"]:
|
||||||
lines.append("Qualité non renseignée : " + ", ".join(
|
lines.append("Qualité non renseignée : " + ", ".join(
|
||||||
f"{c_}_{r_}" for c_, r_ in zq["missing_quality"]))
|
f"{c_}_{r_}" for c_, r_ in zq["missing_quality"]))
|
||||||
ty = min(my, ly) - 3.5
|
ty = min(my, ly) - 3.5
|
||||||
c.setFont("Helvetica", 5.8)
|
font_q, size_q = "Helvetica", 5.8
|
||||||
|
c.setFillColor(black); c.setFont(font_q, size_q)
|
||||||
|
max_w = w * mm
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
sep = ", " if ", " in line else " "
|
||||||
|
for part in _wrap_text(c, line, font_q, size_q, max_w, sep=sep):
|
||||||
if ty < y + 1:
|
if ty < y + 1:
|
||||||
break
|
return
|
||||||
c.drawString(x * mm, ty * mm, _pdf_text(line))
|
c.drawString(x * mm, ty * mm, _pdf_text(part))
|
||||||
ty -= 2.9
|
ty -= 2.9
|
||||||
|
|
||||||
|
|
||||||
@ -498,8 +561,9 @@ def _draw_cartouche(c, box, lay, bbox, cx, cy, title, now, mm):
|
|||||||
"""Titre, échelle graphique et numérique, nord, date, source."""
|
"""Titre, échelle graphique et numérique, nord, date, source."""
|
||||||
from reportlab.lib.colors import black, white
|
from reportlab.lib.colors import black, white
|
||||||
x, y, w, h = box
|
x, y, w, h = box
|
||||||
c.setFillColor(black); c.setFont("Helvetica-Bold", 10)
|
txt, title_size = _fit_title(c, _pdf_text(title), "Helvetica-Bold", 10, w * mm)
|
||||||
c.drawString(x * mm, (y + h - 5) * mm, _pdf_text(title)[:80])
|
c.setFillColor(black); c.setFont("Helvetica-Bold", title_size)
|
||||||
|
c.drawString(x * mm, (y + h - 5) * mm, txt)
|
||||||
c.setFont("Helvetica", 7)
|
c.setFont("Helvetica", 7)
|
||||||
c.drawString(x * mm, (y + h - 9) * mm,
|
c.drawString(x * mm, (y + h - 9) * mm,
|
||||||
_pdf_text(f"Échelle 1:{_fmt_int(lay.scale)} - {lay.paper} {lay.orient} - {lay.dpi} dpi"))
|
_pdf_text(f"Échelle 1:{_fmt_int(lay.scale)} - {lay.paper} {lay.orient} - {lay.dpi} dpi"))
|
||||||
@ -515,26 +579,31 @@ def _draw_cartouche(c, box, lay, bbox, cx, cy, title, now, mm):
|
|||||||
c.drawString(bx * mm, (by - 2.8) * mm, "0")
|
c.drawString(bx * mm, (by - 2.8) * mm, "0")
|
||||||
c.drawRightString((bx + bar_mm) * mm, (by - 2.8) * mm, f"{_fmt_int(length)} m")
|
c.drawRightString((bx + bar_mm) * mm, (by - 2.8) * mm, f"{_fmt_int(length)} m")
|
||||||
# Flèche du nord géographique (la carte est orientée nord du quadrillage)
|
# Flèche du nord géographique (la carte est orientée nord du quadrillage)
|
||||||
gamma = _convergence_deg(cx, cy)
|
gamma = _north_arrow_angle(cx, cy)
|
||||||
ax, ay = x + w - 8, y + h - 12
|
ax, ay = x + w - 8, y + h - 12
|
||||||
c.saveState(); c.translate(ax * mm, ay * mm); c.rotate(-gamma)
|
c.saveState(); c.translate(ax * mm, ay * mm); c.rotate(gamma)
|
||||||
p = c.beginPath(); p.moveTo(0, 5 * mm); p.lineTo(-1.8 * mm, -3 * mm); p.lineTo(0, -1.5 * mm)
|
p = c.beginPath(); p.moveTo(0, 5 * mm); p.lineTo(-1.8 * mm, -3 * mm); p.lineTo(0, -1.5 * mm)
|
||||||
p.lineTo(1.8 * mm, -3 * mm); p.close()
|
p.lineTo(1.8 * mm, -3 * mm); p.close()
|
||||||
c.drawPath(p, stroke=0, fill=1)
|
c.drawPath(p, stroke=0, fill=1)
|
||||||
c.setFont("Helvetica-Bold", 6); c.drawCentredString(0, 6 * mm, "N")
|
c.setFont("Helvetica-Bold", 6); c.drawCentredString(0, 6 * mm, "N")
|
||||||
c.restoreState()
|
c.restoreState()
|
||||||
c.setFont("Helvetica", 5.5)
|
# Note d'orientation : ligne à part (jamais sur la même hauteur que l'échelle
|
||||||
c.drawRightString((x + w) * mm, (ay - 6) * mm,
|
# numérique — évite le chevauchement des deux textes alignés à droite/gauche).
|
||||||
_pdf_text(f"convergence L93 {gamma:+.2f}°".replace(".", ",")))
|
side = "ouest" if gamma >= 0 else "est"
|
||||||
|
north_label = f"nord géographique à {abs(gamma):.2f}° à l'{side} du quadrillage".replace(".", ",")
|
||||||
ty = by - 7
|
ty = by - 7
|
||||||
c.setFont("Helvetica", 6)
|
font_c, size_c = "Helvetica", 6
|
||||||
for line in (f"Centre L93 : X {_fmt_int(round(cx))} m Y {_fmt_int(round(cy))} m",
|
c.setFillColor(black); c.setFont(font_c, size_c)
|
||||||
|
max_w_c = w * mm
|
||||||
|
for line in (north_label,
|
||||||
|
f"Centre L93 : X {_fmt_int(round(cx))} m Y {_fmt_int(round(cy))} m",
|
||||||
f"Zone : {_fmt_int(round(bbox[2] - bbox[0]))} x {_fmt_int(round(bbox[3] - bbox[1]))} m",
|
f"Zone : {_fmt_int(round(bbox[2] - bbox[0]))} x {_fmt_int(round(bbox[3] - bbox[1]))} m",
|
||||||
f"Exporté le {now:%d/%m/%Y %H:%M}",
|
f"Exporté le {now:%d/%m/%Y %H:%M}",
|
||||||
"Source : LiDAR HD (c) IGN - rendu lidar_rendu"):
|
"Source : LiDAR HD (c) IGN - rendu lidar_rendu"):
|
||||||
|
for part in _wrap_text(c, line, font_c, size_c, max_w_c):
|
||||||
if ty < y + 1:
|
if ty < y + 1:
|
||||||
break
|
return
|
||||||
c.drawString(x * mm, ty * mm, _pdf_text(line))
|
c.drawString(x * mm, ty * mm, _pdf_text(part))
|
||||||
ty -= 3.0
|
ty -= 3.0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -203,7 +203,7 @@ def test_build_pdf_a4_landscape(tmp_path, monkeypatch):
|
|||||||
w, h = _mediabox(pdf)
|
w, h = _mediabox(pdf)
|
||||||
assert abs(w - 841.89) < 0.5 and abs(h - 595.28) < 0.5
|
assert abs(w - 841.89) < 0.5 and abs(h - 595.28) < 0.5
|
||||||
assert name.startswith("relief_1054.") and name.endswith("_1-2000.pdf")
|
assert name.startswith("relief_1054.") and name.endswith("_1-2000.pdf")
|
||||||
for s in (b"Prospection ? bois", b"1:2 000", b"Densit", b"2023-03-15", b"LiDAR HD"):
|
for s in (b"Prospection ? bois", b"1:2 000", b"Densit", b"2023-03-15", b"LiDAR HD", b"6,5 pts"):
|
||||||
assert s in pdf, s
|
assert s in pdf, s
|
||||||
|
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ def test_build_pdf_partial_zone_hatched(tmp_path, monkeypatch):
|
|||||||
lon, lat = _transformer("EPSG:2154", "EPSG:4326").transform(1055000.0, 6881500.0)
|
lon, lat = _transformer("EPSG:2154", "EPSG:4326").transform(1055000.0, 6881500.0)
|
||||||
pdf, _ = build_pdf(tmp_path, lat, lon, "A4", "paysage", 2000, compress=False)
|
pdf, _ = build_pdf(tmp_path, lat, lon, "A4", "paysage", 2000, compress=False)
|
||||||
assert pdf.startswith(b"%PDF") and b"1055_6882" in pdf
|
assert pdf.startswith(b"%PDF") and b"1055_6882" in pdf
|
||||||
assert b"non renseign" in pdf
|
assert b"Donn" in pdf and b"manquante" in pdf
|
||||||
|
|
||||||
|
|
||||||
def test_build_pdf_no_data_raises(tmp_path):
|
def test_build_pdf_no_data_raises(tmp_path):
|
||||||
@ -241,3 +241,56 @@ def test_build_pdf_no_data_raises(tmp_path):
|
|||||||
def test_fmt_int():
|
def test_fmt_int():
|
||||||
from lidar_pipeline.export_pdf import _fmt_int
|
from lidar_pipeline.export_pdf import _fmt_int
|
||||||
assert _fmt_int(2000) == "2 000" and _fmt_int(500) == "500"
|
assert _fmt_int(2000) == "2 000" and _fmt_int(500) == "500"
|
||||||
|
|
||||||
|
|
||||||
|
def test_north_arrow_angle_matches_pyproj_reference():
|
||||||
|
"""L'angle de rotation reportlab doit amener la flèche (dessinée vers le
|
||||||
|
nord du quadrillage) sur le nord géographique, dans le bon sens, à l'est
|
||||||
|
et à l'ouest du méridien central (3°E)."""
|
||||||
|
import math
|
||||||
|
from lidar_pipeline.export_pdf import _north_arrow_angle, _to_wgs84
|
||||||
|
from lidar_pipeline.tiles import _transformer, wgs84_to_l93
|
||||||
|
|
||||||
|
to_l93 = _transformer("EPSG:4326", "EPSG:2154")
|
||||||
|
|
||||||
|
def _reference_angle(cx, cy):
|
||||||
|
lat0, lon0 = _to_wgs84(cx, cy)
|
||||||
|
x0, y0 = to_l93.transform(lon0, lat0)
|
||||||
|
x1, y1 = to_l93.transform(lon0, lat0 + 0.001)
|
||||||
|
# azimut (sens horaire depuis le nord du quadrillage) du nord géographique
|
||||||
|
psi = math.degrees(math.atan2(x1 - x0, y1 - y0))
|
||||||
|
return -psi # rotation reportlab (antihoraire) amenant "haut" sur ce nord
|
||||||
|
|
||||||
|
east = wgs84_to_l93(6.0, 46.0) # à l'est du méridien central
|
||||||
|
west = wgs84_to_l93(0.0, 46.0) # à l'ouest du méridien central
|
||||||
|
for cx, cy in (east, west):
|
||||||
|
assert abs(_north_arrow_angle(cx, cy) - _reference_angle(cx, cy)) < 0.05
|
||||||
|
assert _north_arrow_angle(*east) > 0
|
||||||
|
assert _north_arrow_angle(*west) < 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrap_text_lines_fit_width():
|
||||||
|
from reportlab.pdfgen import canvas as rl_canvas
|
||||||
|
from reportlab.lib.units import mm
|
||||||
|
from io import BytesIO
|
||||||
|
from lidar_pipeline.export_pdf import _wrap_text
|
||||||
|
c = rl_canvas.Canvas(BytesIO())
|
||||||
|
text = "Donnée manquante (sans relief) : " + ", ".join(
|
||||||
|
f"{1050 + i}_6882" for i in range(12))
|
||||||
|
max_width = 60 * mm
|
||||||
|
lines = _wrap_text(c, text, "Helvetica", 5.8, max_width, sep=", ")
|
||||||
|
assert len(lines) > 1
|
||||||
|
for line in lines:
|
||||||
|
assert c.stringWidth(line, "Helvetica", 5.8) <= max_width + 1e-6
|
||||||
|
|
||||||
|
|
||||||
|
def test_fit_title_shrinks_then_elides():
|
||||||
|
from reportlab.pdfgen import canvas as rl_canvas
|
||||||
|
from reportlab.lib.units import mm
|
||||||
|
from io import BytesIO
|
||||||
|
from lidar_pipeline.export_pdf import _fit_title
|
||||||
|
c = rl_canvas.Canvas(BytesIO())
|
||||||
|
long_title = "Relief orienté - " + ", ".join(f"{1050 + i:04d}_6882" for i in range(6))
|
||||||
|
text, size = _fit_title(c, long_title, "Helvetica-Bold", 10, 60 * mm, min_size=7.0)
|
||||||
|
assert size >= 7.0
|
||||||
|
assert c.stringWidth(text, "Helvetica-Bold", size) <= 60 * mm + 1e-6
|
||||||
|
|||||||
Reference in New Issue
Block a user