Rhino Sync
06-plugin/help/rhino-sync/en · online versionWhat Rhino Sync is¶
Draw or correct in Rhino 8 and see the result in QGIS; send layers from QGIS and see them in Rhino, with no export / import steps. The link is independent of MapQuilt: the same connection works in any Rhino + QGIS project (a masterplan, an urban design sketch, a measured survey). MapQuilt only embeds it in its dock, as a tab.
"Live" does not mean a socket but two file watchers on the same folder: Rhino writes rhino_push.geojson, QGIS loads it within 0.5 s; QGIS writes qgis_pull.geojson, Rhino draws it. That is enough for working on the same machine or on a shared folder.
Who made it¶
Rhino Sync, like MapQuilt, was created by Radu Andrei, of the "Gheorghe Asachi" Technical University of Iași, Faculty of Architecture "G. M. Cantacuzino", with the help of Claude, the AI model by Anthropic, which wrote the code, tests and documentation under the author's guidance, on the scans of the ICPROM Iași cadastral plans (1988–1990) and on the benchmark drawn by the author in Rhino. The research questions, the method decisions, the ground-truth verification and every confirmation on the real sheets are the author's; the test reports in the repository (T1–T19) record what was tried and with what figures.
The user rule¶
You draw on SCHITA, you read CONTEXT, and QGIS answers you.
| Rhino layer family | Who writes it | What it means |
|---|---|---|
SCHITA::<CLASS>[::<SUBTYPE>] |
you | the only family that leaves on push (SCHITA = sketch); the second level is the class (SCHITA::CLADIRI → building), the third the subtype (SCHITA::CLADIRI::P+4 → subtype P+4, and RH if it is a height regime) |
CONTEXT::<LAYER> |
QGIS → Rhino ("Send context") | the existing situation (CONTEXT = context; cadastre, buildings, boundaries…), drawn locked, replaced at every send, never exported |
QGIS::<LAYER> |
QGIS → Rhino ("Send to Rhino") | the answers: what was taken over / edited in QGIS; replaced at every pull, never exported |
PARCELE, CLADIRI … (plain) |
older drawings | the plain convention still works |
Every entity sent carries rhino_id (the GUID of the Rhino object), class, subtype, RH, and the fills (fill_pattern, fill_color, fill_scale, fill_angle).
Georeferencing convention¶
The Rhino model is in meters, and the coordinates are those of the projected CRS (Stereo 70 by default, EPSG:3844): the origin is not moved and no transformation is applied. The point 695 500 / 633 200 in the drawing is the point 695 500 / 633 200 on the map. EarthAnchorPoint is ignored (the command warns if it is set); a document that is not in meters gets a warning.
Installation¶
With one click¶
08-rhino\installer\dist\RhinoSyncSetup.exe copies the scripts to Documents\rhino-sync, writes the aliases RhinoSync, RhinoSyncPush, RhinoSyncPull, RhinoSyncWatch into the Rhino 8 settings and puts the Rhino Sync plugin into the QGIS profile, enabled. Close Rhino and QGIS first (the aliases are written to settings-Scheme__Default.xml, which Rhino rewrites when it closes).
Manually, in Rhino 8¶
- Copy
08-rhino(or at leastmapquilt_rhino.py,RhinoSync.py,MapQuiltPush.py,MapQuiltPull.py,MapQuiltWatch.py) somewhere stable, e.g.Documents\rhino-sync\. - Options › Aliases, one per command (a single pair of quotes; the path is yours):
RhinoSync ! _-RunPythonScript "C:\Users\<you>\Documents\rhino-sync\RhinoSync.py"
RhinoSyncPush ! _-RunPythonScript "C:\Users\<you>\Documents\rhino-sync\MapQuiltPush.py"
RhinoSyncPull ! _-RunPythonScript "C:\Users\<you>\Documents\rhino-sync\MapQuiltPull.py"
RhinoSyncWatch ! _-RunPythonScript "C:\Users\<you>\Documents\rhino-sync\MapQuiltWatch.py"
The script runs on CPython 3 (the default in Rhino 8); the panel needs Eto.Forms, hence CPython. The first run on an unsaved document asks for the folder.
Manually, in QGIS¶
Copy 08-rhino/qgis-plugin/rhino_sync into %APPDATA%\QGIS\QGIS4\profiles\default\python\plugins\ and enable Rhino Sync from Plugins › Manage; the dock opens from the Vector menu or from the toolbar. With MapQuilt installed, the same box is the Rhino sync tab of the MapQuilt dock.
The shared folder¶
By default the folder is rhino-sync next to the .3dm file; Rhino remembers it in the document's user text, QGIS in its settings. One folder = one project.
<folder>/rhino_push.geojson written by Rhino, read by QGIS
<folder>/qgis_pull.geojson written by QGIS, read by Rhino (onto QGIS::*)
<folder>/qgis_context.geojson written by QGIS, read by Rhino (onto CONTEXT::*, locked)
<folder>/.alive-rhino, .alive-qgis heartbeat of each side while it is on Play (3 s)
<folder>/.mapquilt-sync.json sidecar (who, when, what, CRS, layer map)
<folder>/sync-config.json optional: CRS, layer map, parent layer names, styles
<folder>/rhino_sync.gpkg created by QGIS: the sketch mirror, one layer per class (draft_*)
<folder>/project.gpkg created by QGIS on Promote: the project tables, with rhino_id
Files are written atomically (temporary file + replace, retrying for one second if Windows answers "Access is denied" because the other side is reading).
The Play panel¶
The same on both sides.
Rhino: RhinoSync opens a modeless "Rhino sync" window: Project folder (+ "…"), a lamp with a caption, Play / Stop, Send now, Get now, Get context and the last message. Play = live mode (automatic push 1.5 s after the last change, automatic pull and context, heartbeat in .alive-rhino); the lamp is green while QGIS is on Play too. Running the command again brings the window to the front instead of opening another one.
QGIS: the "Rhino sync" dock (or the tab in MapQuilt) has the same elements: Project folder, lamp, Play / Stop, Send to Rhino, Get now, plus Send context, Promote to project, Fields…, Send image. Play = watching the folder (every push from Rhino loads by itself) and the heartbeat in .alive-qgis; the Play state is remembered between sessions.
The typical cycle¶
- In Rhino:
RhinoSync› Play. In QGIS: Play. - Draw on
SCHITA::PARCELEorSCHITA::CLADIRI::P+4; a second or two after you lift your hand, the parcel appears in QGIS, in the Rhino › Draft from Rhino group, over the plan sheet and the satellite image. - Correct a face in QGIS, then Send to Rhino: you see it at once on
QGIS::PARCELE, in the color of the QGIS symbol. - Send context sends the selected layers (the existing cadastre, the buildings) to
CONTEXT::<LAYER NAME>, locked in Rhino.
Draft / Project in QGIS¶
- The Rhino › Draft from Rhino group: the sketch mirror,
draft_<class>layers inrhino_sync.gpkg, read-only, replaced at every push, styled by class (categorized renderer onfill_pattern). - Promote to project copies the features from the mirror (those selected in the
draft_*layers, or all of them) intoproject.gpkg, group Rhino › Project, one table per class (parcel,building,indicator,zf_propusa…) withrhino_id,class,subtype,RH,code,radius_m,area_m2,source,promoted_at,note, the fills and any columns you add. A feature already promoted (samerhino_id) is updated, not duplicated. - Send to Rhino sends the selected layers (by default all vector layers outside the mirror, so the project tables too) to
QGIS::*.
Labels, fields, image¶
- Fields…: a window listing the layers and their fields, with checkboxes. The checked fields go to Rhino at every Send: the first checked field becomes a text centered in the polygon (height
label_height_m, 2 m by default, in the QGIS label color), and all checked fields become attributes (user text on the object). - Text edited in Rhino comes back to QGIS: label texts remember their field and feature; on push, the changed ones go into
rhino_push.geojsonasmapquilt.edits, and QGIS writes the new value into the field. - Send image renders the raster layers (the selected ones, otherwise the visible ones: XYZ satellite, orthophoto) over the extent of the selected vector layers or, with no selection, over what the map shows, +30 % on each side, at 0.5 m/pixel (at most 4096 px), in the project CRS; Rhino places it as a locked
PictureFrameonCONTEXT::IMAGINE.
Conventions¶
Layers and geometries¶
| Rhino layer | class | geometry |
|---|---|---|
STRAZI |
street | closed polyline |
PARCELE |
parcel (includes the building) | closed polyline |
CLADIRI |
building | closed polyline |
ANEXE |
annex | closed polyline |
AMENAJARI |
amenity | closed polyline |
CODURI |
indicator | circle (→ point with radius_m) or text (→ point with code) |
| any | the same class | hatch → polygon with holes + fill (fill_pattern, fill_color, fill_scale, fill_angle) |
Only closed outlines go through (an open polyline with a gap under 1 m is closed automatically and reported; over 1 m it is skipped). Curves that are not polylines (splines, polycurves with arcs) are sampled at 200 vertices; NURBS circles are recognized by circle fitting. Nested layers go by their last name (Default::PARCELE = PARCELE). A SCHITA::<unmapped name> layer gives the class <name> in lower case (SCHITA::ZF_PROPUSA → zf_propusa).
Fills and colors, both ways¶
- Rhino → QGIS. A hatch becomes a polygon with its holes and with
fill_pattern(Solid,Hatch1,Hatch2,Hatch3,HatchDash,Grid,Grid60,Plus,Squares; a custom pattern is sent asHatch1),fill_color(#rrggbb, by object or by layer),fill_scale,fill_angle. QGIS draws them with a categorized renderer. - QGIS → Rhino. Send to Rhino reads the symbol of each feature (single symbol, categorized, rule-based) and translates it into the closest Rhino pattern: simple fill →
Solid; line pattern fill →Hatch1/HatchDash(angle and spacing are kept; two families at 90° →Grid, three at 60° →Grid60); outline and fill colors are kept. A QGIS layer that is not a class goes toQGIS::<ITS NAME>(no diacritics, upper case), in the symbol color. nominal_scale(1000 by default) sets the paper scale at which the millimeters of QGIS symbols become meters in Rhino (1 mm = 1 m).
sync-config.json¶
QGIS creates it with the default values when you choose the folder; you can edit it. Both sides read it.
{"crs": "EPSG:3844",
"layers": {"STRAZI": "street", "PARCELE": "parcel", "CLADIRI": "building", "ANEXE": "annex", "AMENAJARI": "amenity", "CODURI": "indicator"},
"all_layers": false,
"draft_parent_layer": "SCHITA", "context_parent_layer": "CONTEXT", "pull_parent_layer": "QGIS",
"qgis_group": "Rhino",
"style": {"parcel": {"color": "#ffc800", "fill_pattern": "Solid", "opacity": 0.16, "stroke_width": 0.5},
"building": {"color": "#dc0000", "fill_pattern": "Hatch1", "opacity": 0.4, "stroke_width": 0.5}},
"color_by": "object",
"label_height_m": 2,
"nominal_scale": 1000}
crs: any projected CRS in meters; the Rhino model must be at its coordinates.layers: your project's layer → class map;all_layers: trueexports all Rhino layers, with class = layer name (useful for a masterplan with dozens of layers).style: the default look of each class (color, pattern, opacity, stroke width), read by both sides.color_by:object(objects pulled from QGIS keep their own color) orlayer(everything in the layer color).
CLI without Rhino, batch checks¶
mapquilt_rhino.py also works without Rhino, with rhino3dm:
python 08-rhino\mapquilt_rhino.py push desen.3dm C:\tmp\sync\rhino_push.geojson [--all-layers] [--crs=EPSG:xxxx]
python 08-rhino\mapquilt_rhino.py pull qgis_pull.geojson out.3dm [--plain]
On the Țicău benchmark (cadru_ticau.3dm), push gives exactly the benchmark counts (3 streets, 32 parcels, 47 buildings, 27 annexes, 7 amenities, 41 indicators), with vertices within 1 mm of etalon_ticau.geojson. pull draws into a new .3dm; with --plain it draws on the base layers, not under QGIS::, so that it can be re-exported identically.
Limits¶
- Closed outlines, circles, texts and hatches go through; open lines, blocks and surfaces do not (they are reported).
- The holes of a polygon without a fill go from QGIS to Rhino as separate polylines; with a fill, they become the holes of the hatch.
Hatch2/Hatch3are approximated in QGIS by denser simple lines and come back asHatch1. Gradients, SVGs and raster fills from QGIS becomeSolidor the closest pattern. Opacity does not exist in Rhino; it is kept only as an attribute.- Hatches need Rhino: the CLI with
rhino3dmcan neither read them (reported on push) nor create them (on pull it draws the outline; the fill stays in themapquilt_filluser text). - Pull replaces everything on
QGIS::*; move what you want to keep onto your own layers. - No topology check during sync (overlaps, gaps); that stays in QGIS / in the Engine.
- Z is lost (everything is at 0); texts are 1 m high (labels 2 m), Arial.
- The automatic push exports the whole document at every 1.5 s pause; on drawings with thousands of objects, set
PUSH_DEBOUNCE_Shigher or useRhinoSyncPushmanually. rhino_idis the object's GUID: a copy, or a command that recreates the object (Trim, Join), gives a new id, hence a new feature in the project; editing control points keeps it. There is no conflict detection yet (the same feature changed on both sides).- QGIS does not start Rhino and cannot stop it; the two applications see each other only through the
.alive-*files.
Literature¶
- McNeel, Rhino 8 — RhinoCommon and rhino3dm, https://developer.rhino3d.com. The script runs on CPython 3 in Rhino 8; the CLI on
rhino3dm. - Howard Butler et al., The GeoJSON Format, RFC 7946 (IETF, 2016), https://doi.org/10.17487/RFC7946. The format of the files in the shared folder (with the
crsmember for the projected CRS). - OGC, GeoPackage Encoding Standard 1.3, https://www.geopackage.org. The sketch mirror and the project tables.
- QGIS Development Team, QGIS Geographic Information System, https://qgis.org. The categorized and rule-based renderers read when sending.
- MapQuilt, Legătura live Rhino ↔ QGIS [The live Rhino ↔ QGIS link] (29–31 October 2026),
08-rhino/CITESTE-MA.md(in Romanian), and Necesitățile urbaniștilor [What urban planners need],CERCETARE-necesitati-urbanisti.md§5, the layer families (in Romanian).
Changelog¶
From the plugin's metadata.txt, newest first.
- 0.2.22026-11-05 — The four buttons Send context, Send image, Fields..., Promote to project in a 2 x 2 grid, so a narrow dock needs no sideways scrolling.
- 0.2.12026-11-04 — A '?' button in the dock opens the manual shipped with the plugin (help/index.html, generated from 06-plugin/help/rhino-sync; also online).
- 0.2.02026-10-31 — Panel Play, layer families SCHITA / CONTEXT / QGIS, fills both ways, Fields, Send image.
Rhino Sync
06-plugin/help/rhino-sync/ro · varianta onlineCe este Rhino Sync¶
Desenați sau corectați în Rhino 8 și vedeți rezultatul în QGIS; trimiteți straturi din QGIS și le vedeți în Rhino, fără pași de export / import. Legătura este independentă de MapQuilt: aceeași conexiune merge în orice proiect Rhino + QGIS (un masterplan, o schiță de urbanism, un releveu). MapQuilt doar o încorporează în dock-ul său, ca tab.
„Live” nu înseamnă un socket, ci două supraveghetoare de fișiere pe același folder: Rhino scrie rhino_push.geojson, QGIS îl încarcă în 0,5 s; QGIS scrie qgis_pull.geojson, Rhino îl desenează. E suficient pentru lucrul pe aceeași mașină sau pe un folder partajat.
Cine l-a făcut¶
Rhino Sync, ca și MapQuilt, este creat de Radu Andrei, de la Universitatea Tehnică „Gheorghe Asachi” din Iași, Facultatea de Arhitectură „G. M. Cantacuzino”, cu ajutorul lui Claude, modelul de inteligență artificială al companiei Anthropic, care a scris codul, testele și documentația sub îndrumarea autorului, pe scanările planurilor cadastrale ICPROM Iași (1988–1990) și pe etalonul desenat de autor în Rhino. Întrebările cercetării, deciziile de metodă, verificarea adevărului din teren și fiecare confirmare pe foile reale sunt ale autorului; rapoartele de test din depozit (T1–T19) consemnează ce s-a încercat și cu ce cifre.
Regula pentru utilizator¶
Desenezi pe SCHITA, citești CONTEXT, iar QGIS îți răspunde.
| Familie de straturi Rhino | Cine o scrie | Ce înseamnă |
|---|---|---|
SCHITA::<CLASA>[::<SUBTIP>] |
dvs. | singura familie care pleacă la push; al doilea nivel e clasa (SCHITA::CLADIRI → building), al treilea subtipul (SCHITA::CLADIRI::P+4 → subtype P+4, și RH dacă e regim de înălțime) |
CONTEXT::<STRAT> |
QGIS → Rhino („Send context”) | situația existentă (cadastru, clădiri, limite…), desenată blocată, înlocuită la fiecare trimitere, niciodată exportată |
QGIS::<STRAT> |
QGIS → Rhino („Send to Rhino”) | răspunsurile: ce a fost preluat / editat în QGIS; înlocuite la fiecare pull, niciodată exportate |
PARCELE, CLADIRI … (simple) |
desenele vechi | convenția simplă merge în continuare |
Fiecare entitate trimisă poartă rhino_id (GUID-ul obiectului Rhino), class, subtype, RH, umplerile (fill_pattern, fill_color, fill_scale, fill_angle).
Convenția de georeferențiere¶
Modelul Rhino este în metri, iar coordonatele sunt cele ale CRS-ului proiectat (implicit Stereo 70, EPSG:3844): nu se mută originea, nu se aplică nicio transformare. Punctul din desen 695 500 / 633 200 este punctul de pe hartă 695 500 / 633 200. EarthAnchorPoint e ignorat (comanda avertizează dacă e setat); un document care nu e în metri primește un avertisment.
Instalare¶
Cu un clic¶
08-rhino\installer\dist\RhinoSyncSetup.exe copiază scripturile în Documents\rhino-sync, scrie aliasurile RhinoSync, RhinoSyncPush, RhinoSyncPull, RhinoSyncWatch în setările Rhino 8 și pune pluginul Rhino Sync în profilul QGIS, activat. Închideți Rhino și QGIS înainte (aliasurile se scriu în settings-Scheme__Default.xml, pe care Rhino îl rescrie la închidere).
Manual, în Rhino 8¶
- Copiați
08-rhino(sau măcarmapquilt_rhino.py,RhinoSync.py,MapQuiltPush.py,MapQuiltPull.py,MapQuiltWatch.py) undeva stabil, de ex.Documents\rhino-sync\. - Options › Aliases, câte unul pe comandă (o singură pereche de ghilimele, calea e a dvs.):
RhinoSync ! _-RunPythonScript "C:\Users\<dvs>\Documents\rhino-sync\RhinoSync.py"
RhinoSyncPush ! _-RunPythonScript "C:\Users\<dvs>\Documents\rhino-sync\MapQuiltPush.py"
RhinoSyncPull ! _-RunPythonScript "C:\Users\<dvs>\Documents\rhino-sync\MapQuiltPull.py"
RhinoSyncWatch ! _-RunPythonScript "C:\Users\<dvs>\Documents\rhino-sync\MapQuiltWatch.py"
Scriptul rulează pe CPython 3 (implicit în Rhino 8); panoul cere Eto.Forms, deci CPython. Prima rulare pe un document nesalvat cere folderul.
Manual, în QGIS¶
Copiați 08-rhino/qgis-plugin/rhino_sync în %APPDATA%\QGIS\QGIS4\profiles\default\python\plugins\ și activați Rhino Sync din Plugins › Manage; dock-ul apare din meniul Vector sau din bara de unelte. Cu MapQuilt instalat, aceeași casetă e tabul Rhino sync al dock-ului MapQuilt.
Folderul comun¶
Implicit, folderul este rhino-sync lângă fișierul .3dm; Rhino îl ține minte în textul-utilizator al documentului, QGIS în setări. Un folder = un proiect.
<folder>/rhino_push.geojson scris de Rhino, citit de QGIS
<folder>/qgis_pull.geojson scris de QGIS, citit de Rhino (pe QGIS::*)
<folder>/qgis_context.geojson scris de QGIS, citit de Rhino (pe CONTEXT::*, blocat)
<folder>/.alive-rhino, .alive-qgis bătaia de inimă a fiecărei părți cât e pe Play (3 s)
<folder>/.mapquilt-sync.json însoțitor (cine, când, ce, CRS, harta straturilor)
<folder>/sync-config.json opțional: CRS, harta straturilor, numele părinților, stiluri
<folder>/rhino_sync.gpkg creat de QGIS: oglinda schiței, un strat pe clasă (draft_*)
<folder>/project.gpkg creat de QGIS la Promote: tabelele proiectului, cu rhino_id
Fișierele se scriu atomic (temporar + înlocuire, cu reîncercare o secundă dacă Windows răspunde „Access is denied” pentru că cealaltă parte citește).
Panoul Play¶
Același pe ambele părți.
Rhino: RhinoSync deschide o fereastră nemodală „Rhino sync”: Project folder (+ „…”), o lampă cu text, Play / Stop, Send now, Get now, Get context și ultimul mesaj. Play = modul live (push automat la 1,5 s după ultima modificare, pull și context automate, bătaie de inimă în .alive-rhino); lampa e verde cât QGIS e și el pe Play. A doua comandă aduce fereastra în față, nu deschide alta.
QGIS: dock-ul „Rhino sync” (sau tabul din MapQuilt) are aceleași elemente: Project folder, lampă, Play / Stop, Send to Rhino, Get now, plus Send context, Promote to project, Fields…, Send image. Play = supravegherea folderului (fiecare push din Rhino se încarcă singur) și bătaia de inimă în .alive-qgis; starea Play se ține minte între sesiuni.
Ciclul tipic¶
- În Rhino:
RhinoSync› Play. În QGIS: Play. - Desenați pe
SCHITA::PARCELEsauSCHITA::CLADIRI::P+4; la o secundă-două după ce ridicați mâna, parcela apare în QGIS, în grupul Rhino › Draft from Rhino, peste planșă și satelit. - Corectați în QGIS o față, Send to Rhino: o vedeți imediat pe
QGIS::PARCELE, în culoarea simbolului QGIS. - Send context trimite straturile selectate (cadastrul existent, clădirile) pe
CONTEXT::<NUME STRAT>, blocate în Rhino.
Draft / Project în QGIS¶
- Grupul Rhino › Draft from Rhino: oglinda schiței, straturi
draft_<clasă>înrhino_sync.gpkg, doar citire, înlocuite la fiecare push, stilizate pe clasă (randor pe categorii dupăfill_pattern). - Promote to project copiază entitățile din oglindă (cele selectate în straturile
draft_*, sau toate) înproject.gpkg, grupul Rhino › Project, câte un tabel pe clasă (parcel,building,indicator,zf_propusa…) curhino_id,class,subtype,RH,code,radius_m,area_m2,source,promoted_at,note, umplerile și orice coloane adăugați. O entitate deja promovată (acelașirhino_id) e actualizată, nu dublată. - Send to Rhino trimite straturile selectate (implicit toate straturile vectoriale din afara oglinzii, deci și tabelele proiectului) pe
QGIS::*.
Etichete, câmpuri, imagine¶
- Fields…: o fereastră cu straturile și câmpurile lor, cu bife. Câmpurile bifate pleacă în Rhino la fiecare Send: primul câmp bifat devine un text centrat în poligon (înălțime
label_height_m, implicit 2 m, în culoarea etichetei din QGIS), toate câmpurile bifate devin atribute (text-utilizator pe obiect). - Textul editat în Rhino se întoarce în QGIS: textele-etichetă țin minte câmpul și entitatea; la push, cele schimbate pleacă în
rhino_push.geojsoncamapquilt.edits, iar QGIS scrie valoarea nouă în câmp. - Send image randează straturile raster (cele selectate, altfel cele vizibile: satelit XYZ, ortofoto) pe extinderea straturilor vectoriale selectate sau, fără selecție, pe ce arată harta, +30 % pe fiecare latură, la 0,5 m/pixel (cel mult 4096 px), în CRS-ul proiectului; Rhino o așază ca
PictureFrameblocat peCONTEXT::IMAGINE.
Convenții¶
Straturi și geometrii¶
| Strat Rhino | clasă | geometrie |
|---|---|---|
STRAZI |
street | polilinie închisă |
PARCELE |
parcel (include clădirea) | polilinie închisă |
CLADIRI |
building | polilinie închisă |
ANEXE |
annex | polilinie închisă |
AMENAJARI |
amenity | polilinie închisă |
CODURI |
indicator | cerc (→ punct cu radius_m) sau text (→ punct cu code) |
| oricare | aceeași clasă | hașură → poligon cu goluri + umplere (fill_pattern, fill_color, fill_scale, fill_angle) |
Numai contururi închise trec (o polilinie deschisă cu golul sub 1 m se închide automat și se raportează; peste 1 m se sare). Curbele care nu sunt polilinii (spline, polycurve cu arce) se eșantionează în 200 de vârfuri; cercurile NURBS se recunosc prin potrivire de cerc. Straturile imbricate merg după ultimul nume (Default::PARCELE = PARCELE). Un strat SCHITA::<nume nemapat> dă clasa <nume> cu minuscule (SCHITA::ZF_PROPUSA → zf_propusa).
Umpleri și culori, în ambele sensuri¶
- Rhino → QGIS. O hașură devine un poligon cu golurile ei și cu
fill_pattern(Solid,Hatch1,Hatch2,Hatch3,HatchDash,Grid,Grid60,Plus,Squares; un model propriu se trimite caHatch1),fill_color(#rrggbb, pe obiect sau pe strat),fill_scale,fill_angle. QGIS le desenează cu un randor pe categorii. - QGIS → Rhino. Send to Rhino citește simbolul fiecărei entități (simbol unic, categorii, reguli) și îl traduce în cel mai apropiat model Rhino: umplere simplă →
Solid; umplere cu linii →Hatch1/HatchDash(unghiul și distanța se păstrează; două familii la 90° →Grid, trei la 60° →Grid60); culoarea conturului și a umplerii se păstrează. Un strat QGIS care nu e o clasă ajunge peQGIS::<NUMELE LUI>(fără diacritice, majuscule), cu culoarea simbolului. nominal_scale(implicit 1000) spune la ce scară de hârtie milimetrii simbolurilor QGIS devin metri în Rhino (1 mm = 1 m).
sync-config.json¶
QGIS îl creează cu valorile implicite când alegeți folderul; îl puteți edita. Ambele părți îl citesc.
{"crs": "EPSG:3844",
"layers": {"STRAZI": "street", "PARCELE": "parcel", "CLADIRI": "building", "ANEXE": "annex", "AMENAJARI": "amenity", "CODURI": "indicator"},
"all_layers": false,
"draft_parent_layer": "SCHITA", "context_parent_layer": "CONTEXT", "pull_parent_layer": "QGIS",
"qgis_group": "Rhino",
"style": {"parcel": {"color": "#ffc800", "fill_pattern": "Solid", "opacity": 0.16, "stroke_width": 0.5},
"building": {"color": "#dc0000", "fill_pattern": "Hatch1", "opacity": 0.4, "stroke_width": 0.5}},
"color_by": "object",
"label_height_m": 2,
"nominal_scale": 1000}
crs: orice CRS proiectat în metri; modelul Rhino trebuie să fie la coordonatele lui.layers: harta strat → clasă a proiectului dvs.;all_layers: trueexportă toate straturile Rhino, clasa = numele stratului (util pentru un masterplan cu zeci de straturi).style: aspectul implicit al fiecărei clase (culoare, model, opacitate, grosime), citit de ambele părți.color_by:object(obiectele trase din QGIS au culoarea lor) saulayer(totul pe culoarea stratului).
CLI fără Rhino, verificare în loturi¶
mapquilt_rhino.py merge și fără Rhino, cu rhino3dm:
python 08-rhino\mapquilt_rhino.py push desen.3dm C:\tmp\sync\rhino_push.geojson [--all-layers] [--crs=EPSG:xxxx]
python 08-rhino\mapquilt_rhino.py pull qgis_pull.geojson out.3dm [--plain]
Pe etalonul Țicău (cadru_ticau.3dm) push dă exact numărătorile etalonului (3 străzi, 32 parcele, 47 clădiri, 27 anexe, 7 amenajări, 41 indicatori), cu vârfurile la sub 1 mm de etalon_ticau.geojson. pull desenează într-un .3dm nou; cu --plain pe straturile de bază, nu sub QGIS::, ca să poată fi re-exportat identic.
Limite¶
- Trec contururi închise, cercuri, texte și hașuri; linii deschise, blocuri și suprafețe nu trec (sunt raportate).
- Golurile unui poligon fără umplere trec din QGIS în Rhino ca polilinii separate; cu umplere, devin golurile hașurii.
Hatch2/Hatch3se aproximează în QGIS prin linii simple mai dese și se întorc caHatch1. Gradientele, SVG-urile și rastrele din QGIS devinSolidsau cel mai apropiat model. Opacitatea nu există în Rhino; se păstrează doar ca atribut.- Hașurile cer Rhino: CLI-ul cu
rhino3dmnu le poate citi (raportate la push) și nici crea (la pull desenează conturul; umplerea rămâne în textul-utilizatormapquilt_fill). - Pull-ul înlocuiește tot ce e pe
QGIS::*; ce vreți să păstrați mutați pe straturile dvs. - Nicio verificare topologică la sincronizare (suprapuneri, goluri); ea rămâne în QGIS / în Engine.
- Z-ul se pierde (totul e la 0); textele au înălțime 1 m (etichetele 2 m), Arial.
- Push-ul automat exportă tot documentul la fiecare pauză de 1,5 s; pe desene de mii de obiecte setați
PUSH_DEBOUNCE_Smai mare sau folosițiRhinoSyncPushmanual. rhino_ide GUID-ul obiectului: o copie sau o comandă care îl recreează (Trim, Join) dă un id nou, deci o entitate nouă în proiect; editarea punctelor de control îl păstrează. Nu există încă detectarea conflictelor (aceeași entitate schimbată pe ambele părți).- QGIS nu pornește Rhino și nu îl poate opri; cele două aplicații se văd doar prin fișierele
.alive-*.
Literatură¶
- McNeel, Rhino 8 — RhinoCommon și rhino3dm, https://developer.rhino3d.com. Scriptul rulează pe CPython 3 în Rhino 8; CLI-ul pe
rhino3dm. - Howard Butler et al., The GeoJSON Format, RFC 7946 (IETF, 2016), https://doi.org/10.17487/RFC7946. Formatul fișierelor din folderul comun (cu membrul
crspentru CRS-ul proiectat). - OGC, GeoPackage Encoding Standard 1.3, https://www.geopackage.org. Oglinda schiței și tabelele proiectului.
- QGIS Development Team, QGIS Geographic Information System, https://qgis.org. Randoarele pe categorii și pe reguli citite la trimitere.
- MapQuilt, Legătura live Rhino ↔ QGIS (29–31 octombrie 2026),
08-rhino/CITESTE-MA.md, și Necesitățile urbaniștilor,CERCETARE-necesitati-urbanisti.md§5 (familiile de straturi).
Jurnalul versiunilor (changelog)¶
Din metadata.txt al pluginului, cea mai nouă versiune prima (în engleză).
- 0.2.22026-11-05 — The four buttons Send context, Send image, Fields..., Promote to project in a 2 x 2 grid, so a narrow dock needs no sideways scrolling.
- 0.2.12026-11-04 — A '?' button in the dock opens the manual shipped with the plugin (help/index.html, generated from 06-plugin/help/rhino-sync; also online).
- 0.2.02026-10-31 — Panel Play, layer families SCHITA / CONTEXT / QGIS, fills both ways, Fields, Send image.