import json, numpy as np, collections, math from PIL import Image, ImageDraw from shapely.geometry import Polygon, MultiPoint, LineString, Point R = json.load(open("rhino.json")); T = json.load(open("transfo.json")) s, x0, y1, dx, dy = T["s"], T["x0"], T["y1"], T["dx"], T["dy"] f = lambda x, y: ((x - x0) * s + dx, (y1 - y) * s + dy) W, H = 1654, 1170 inimg = lambda x, y: 0 <= f(x, y)[0] <= W and 0 <= f(x, y)[1] <= H # 1. maisons existantes : rectangles de 3_ TRAITS VUS cand = json.load(open("cand_maisons.json")) exist = [Polygon(c["p"]) for c in cand if c["l"] == "3_ TRAITS VUS"] # faîtage : ligne intérieure la plus longue de 3_ TRAITS VUS contenue dans la maison lignes = [LineString(c["p"]) for c in R["courbes"] if c["l"] in ("3_ TRAITS VUS", "4_ TRAITS VUS FIINS") and not c["f"] and len(c["p"]) >= 2] maisons = [] for P in exist: inner = [l for l in lignes if P.buffer(0.3).contains(l) and l.length > 3] mrr = list(P.minimum_rotated_rectangle.exterior.coords)[:4] e1 = np.subtract(mrr[1], mrr[0]); e2 = np.subtract(mrr[2], mrr[1]) if inner: l = max(inner, key=lambda l: l.length); a, b = l.coords[0], l.coords[-1] ang = math.atan2(b[1] - a[1], b[0] - a[0]) else: e = e1 if np.linalg.norm(e1) >= np.linalg.norm(e2) else e2; ang = math.atan2(e[1], e[0]) maisons.append({"type": "existante", "p": [list(map(lambda v: round(v, 2), q)) for q in mrr], "faitage": round(ang, 4)}) # 2. maisons transformées et grand bâtiment : grappes du calque A_ISOL _ COUPE iso = [c for c in R["courbes"] if c["l"] == "A_ISOL _ COUPE"] pts = [(p, i) for i, c in enumerate(iso) for p in c["p"]] centres = [np.mean(np.array(c["p"]), axis=0) for c in iso] # regroupement simple par distance (8 m) groupes = [] for i, cc in enumerate(centres): for g in groupes: if min(np.linalg.norm(cc - centres[j]) for j in g) < 8: g.append(i); break else: groupes.append([i]) # fusion itérative des groupes proches chg = True while chg: chg = False for a in range(len(groupes)): for b in range(a + 1, len(groupes)): if min(np.linalg.norm(centres[i] - centres[j]) for i in groupes[a] for j in groupes[b]) < 8: groupes[a] += groupes[b]; del groupes[b]; chg = True; break if chg: break transfo = [] for g in groupes: P = MultiPoint([tuple(p) for i in g for p in iso[i]["p"]]).minimum_rotated_rectangle if P.area < 60: continue cx, cy = P.centroid.x, P.centroid.y if not inimg(cx, cy): continue mrr = list(P.exterior.coords)[:4] e1 = np.subtract(mrr[1], mrr[0]); e2 = np.subtract(mrr[2], mrr[1]) e = e1 if np.linalg.norm(e1) >= np.linalg.norm(e2) else e2 transfo.append({"type": "grand" if P.area > 300 else "transformee", "p": [[round(v, 2) for v in q] for q in mrr], "faitage": round(math.atan2(e[1], e[0]), 4), "aire": round(P.area, 1)}) print("existantes", len(maisons), "| transformées/grand", [(t["type"], t["aire"]) for t in transfo]) # 3. arbres : instances de blocs, couleur échantillonnée sur le dessin TOTAL tot = np.asarray(Image.open("total100-1.png").convert("RGB")).astype(int) arbres = [] rayons = {"Ishigami's fruit tree's 1": 1.6, "ishigami foret": 2.6, "B_Existantbuisson": 1.2, "A_Existant arbre": 2.4, "0": 2.0, "Ishigami's tree's 2": 2.4} INST = json.load(open("instances_monde.json")) for it in INST: if it["l"] not in rayons or not inimg(it["x"], it["y"]): continue rayons[it["l"]] = rayons[it["l"]] px, py = f(it["x"], it["y"]); r = max(2, int(max(it["r"], 0.8) * s * 0.7)) x_, y_ = int(px), int(py) zone = tot[max(0, y_ - r):y_ + r, max(0, x_ - r):x_ + r].reshape(-1, 3) col = zone[(zone.sum(1) < 690) & (zone.max(1) - zone.min(1) > 18)] c = col.mean(0) if len(col) > 3 else np.array([150, 185, 120]) arbres.append({"x": it["x"], "y": it["y"], "k": it["l"], "r": round(max(it["r"], 0.8), 2), "c": "#%02x%02x%02x" % tuple(int(v) for v in c)}) print("arbres", len(arbres), collections.Counter(a["k"] for a in arbres)) json.dump({"maisons": maisons, "speciaux": transfo, "arbres": arbres}, open("batiments.json", "w")) # contrôle sur TOTAL ov = Image.open("total100-1.png").convert("RGB"); d = ImageDraw.Draw(ov) for m_ in maisons: d.polygon([f(*q) for q in m_["p"]], outline=(0, 60, 230), width=3) for t in transfo: d.polygon([f(*q) for q in t["p"]], outline=(230, 0, 0), width=4) for a in arbres[::3]: px, py = f(a["x"], a["y"]); rr = a["r"] * s * 0.5; d.ellipse([px - rr, py - rr, px + rr, py + rr], outline=(0, 0, 0)) ov.save("controle-batiments.png")