Skip to content

Commit fe0cda7

Browse files
committed
finished tittle box
1 parent 66c63e4 commit fe0cda7

3 files changed

Lines changed: 164 additions & 14 deletions

File tree

cadastral.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,38 @@ def __init__(self, **kwargs):
1313
if self.type != PlanType.CADASTRAL:
1414
raise ValueError("CadastralPlan must have type PlanType.CADASTRAL")
1515

16+
self._frame_x_percent = 0.35
17+
self._frame_y_percent = 0.8
18+
self._frame_coords = self._setup_frame_coords()
19+
if not self._frame_coords:
20+
raise ValueError("Cannot determine frame coordinates without valid coordinates.")
1621
self._drawer = self._setup_drawer()
1722

1823
def _setup_drawer(self) -> SurveyDXFManager:
1924
drawer = SurveyDXFManager(plan_name=self.name, scale=self.get_drawing_scale())
2025
drawer.setup_font(self.font)
2126
drawer.setup_beacon_style(self.beacon_type, self.beacon_size)
27+
drawer.setup_graphical_scale_style(length=(self._frame_coords[2] - self._frame_coords[0]) * 0.4)
2228
return drawer
2329

30+
def _setup_frame_coords(self):
31+
min_x, min_y, max_x, max_y = self.get_bounding_box()
32+
if min_x is None or min_y is None or max_x is None or max_y is None:
33+
return None
34+
35+
width = max_x - min_x
36+
height = max_y - min_y
37+
38+
margin_x = max(width, height) * self._frame_x_percent
39+
margin_y = max(height, width) * self._frame_y_percent
40+
41+
frame_left = min_x - margin_x
42+
frame_bottom = min_y - margin_y
43+
frame_right = max_x + margin_x
44+
frame_top = max_y + margin_y
45+
46+
return frame_left, frame_bottom, frame_right, frame_top
47+
2448
def draw_beacons(self):
2549
if not self.coordinates:
2650
return
@@ -100,40 +124,43 @@ def draw_frames(self):
100124
min_x, min_y, max_x, max_y = self.get_bounding_box()
101125
width, height = max_x - min_x, max_y - min_y
102126

103-
margin_x, margin_y = max(width, height) * 0.35, max(height, width) * 0.7
127+
margin_x, margin_y = max(width, height) * self._frame_x_percent, max(height, width) * self._frame_y_percent
104128
frame_left, frame_bottom = min_x - margin_x, min_y - margin_y
105129
frame_right, frame_top = max_x + margin_x, max_y + margin_y
106130
self._drawer.draw_frame(frame_left, frame_bottom, frame_right, frame_top)
107131

108-
offset_x, offset_y = max(width, height) * 0.38, max(height, width) * 0.73
132+
offset_x, offset_y = max(width, height) * (self._frame_x_percent + 0.03), max(height, width) * (self._frame_y_percent + 0.03)
109133
self._drawer.draw_frame(min_x - offset_x, min_y - offset_y,
110134
max_x + offset_x, max_y + offset_y)
111135

112-
def draw_title(self):
136+
def draw_title_block(self):
113137
"""Add title block to the frame."""
114138
min_x, min_y, max_x, max_y = self.get_bounding_box()
115139
width, height = max_x - min_x, max_y - min_y
116140

117-
margin_x, margin_y = max(width, height) * 0.35, max(height, width) * 0.7
141+
margin_x, margin_y = max(width, height) * self._frame_x_percent, max(height, width) * self._frame_y_percent
118142
frame_left, frame_bottom = min_x - margin_x, min_y - margin_y
119143
frame_right, frame_top = max_x + margin_x, max_y + margin_y
120144

121145
frame_width = frame_right - frame_left
122146
frame_center_x = frame_left + (frame_width / 2)
123147

124148
title_y = frame_top - (margin_y * 0.2)
125-
self._drawer.add_title(html_to_mtext(self.build_title()),
149+
self._drawer.draw_title_block(html_to_mtext(self.build_title()),
126150
frame_center_x,
127151
title_y,
128152
frame_width * 0.6,
129-
self.font_size)
153+
self.font_size,
154+
graphical_scale_length=(self._frame_coords[2] - self._frame_coords[0]) * 0.4,
155+
area=f"AREA :- {self.parcels[0].area} SQ.METRES",
156+
origin=f"ORIGIN :- {self.origin.upper()}")
130157

131158
def draw(self):
132159
# Draw elements
133160
self.draw_beacons()
134161
self.draw_parcels()
135162
self.draw_frames()
136-
self.draw_title()
163+
self.draw_title_block()
137164

138165
def save_dxf(self, file_path: str):
139166
self._drawer.save_dxf(file_path)

dxf.py

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import uuid
1111
import zipfile
1212
from upload import upload_file
13+
from ezdxf import bbox
14+
1315

1416
class SurveyDXFManager:
1517
def __init__(self, plan_name: str = "Survey Plan", scale: float = 1.0):
@@ -83,6 +85,93 @@ def setup_beacon_style(self, type_: str = "box", size: float = 1.0):
8385
path = hatch.paths.add_edge_path()
8486
path.add_arc((0, 0), radius=radius, start_angle=0, end_angle=360)
8587

88+
def setup_graphical_scale_style(self, length: float = 1000):
89+
length = length * self.scale
90+
height = length * 0.05 # 5% of length
91+
92+
interval = length / 5 # 5 intervals
93+
94+
# Create a block for the graphical scale
95+
block = self.doc.blocks.new(name='GRAPHICAL_SCALE')
96+
97+
# draw large rectangle
98+
block.add_lwpolyline(
99+
[(0, 0), (length, 0), (length, height), (0, height)],
100+
close=True,
101+
dxfattribs={'color': 7} # Black/White
102+
)
103+
104+
# draw middle line
105+
block.add_line(
106+
(0, height / 2),
107+
(length, height / 2),
108+
dxfattribs={'color': 7}
109+
)
110+
111+
text_interval = 1000 / self.scale / 10 / 5
112+
113+
# draw interval lines
114+
to_shade = "up"
115+
for i in range(6):
116+
x = i * interval
117+
line_height = height * 1.5
118+
block.add_line(
119+
(x, 0),
120+
(x, line_height),
121+
dxfattribs={'color': 7}
122+
)
123+
124+
text = f"{int((i - 1) * text_interval)}"
125+
alignment = TextEntityAlignment.TOP_CENTER
126+
if i == 0:
127+
text = f"Meters {int(text_interval)}"
128+
alignment = TextEntityAlignment.TOP_RIGHT
129+
if i == 5:
130+
text = f"{int((i - 1) * text_interval)} Meters"
131+
alignment = TextEntityAlignment.TOP_LEFT
132+
133+
134+
# add text above line
135+
block.add_text(
136+
text,
137+
dxfattribs={
138+
'height': height * 0.5,
139+
'color': 7,
140+
'style': 'SURVEY_TEXT'
141+
}
142+
).set_placement(
143+
(x, height * 2.3),
144+
align=alignment
145+
)
146+
147+
148+
if i == 5:
149+
continue
150+
151+
if i == 0:
152+
mini_interval = interval / 2
153+
for j in range(2):
154+
mini_x = j * mini_interval
155+
if to_shade == "up":
156+
# shade first upper half
157+
hatch = block.add_hatch(color=7)
158+
hatch.paths.add_polyline_path([(mini_x, height / 2), (mini_x + mini_interval, height / 2), (mini_x + mini_interval, height), (mini_x, height)])
159+
to_shade = "down"
160+
else:
161+
# shade lower half
162+
hatch = block.add_hatch(color=7)
163+
hatch.paths.add_polyline_path([(mini_x, 0), (mini_x + mini_interval, 0), (mini_x + mini_interval, height / 2), (mini_x, height / 2)])
164+
to_shade = "up"
165+
else:
166+
if to_shade == "up":
167+
hatch = block.add_hatch(color=7)
168+
hatch.paths.add_polyline_path([(x, height / 2), (x + interval, height / 2), (x + interval, height), (x, height)])
169+
to_shade = "down"
170+
else:
171+
hatch = block.add_hatch(color=7)
172+
hatch.paths.add_polyline_path([(x, 0), (x + interval, 0), (x + interval, height / 2), (x, height / 2)])
173+
to_shade = "up"
174+
86175
def setup_font(self, font_name: str = "Times New Roman"):
87176
# Add a new text style with the specified font
88177
self.doc.styles.add('SURVEY_TEXT', font=f'{font_name}.ttf')
@@ -147,7 +236,7 @@ def add_text(self, text: str, x: float, y: float, angle: float = 0.0, height: fl
147236
height = height * self.scale
148237

149238
"""Add arbitrary text at given coordinates with optional rotation"""
150-
self.msp.add_text(
239+
text = self.msp.add_text(
151240
text,
152241
dxfattribs={
153242
'layer': 'LABELS',
@@ -160,22 +249,57 @@ def add_text(self, text: str, x: float, y: float, angle: float = 0.0, height: fl
160249
align=TextEntityAlignment.MIDDLE_CENTER
161250
)
162251

163-
def add_title(self, text: str, x: float, y: float, width: float, title_height: float = 1.0):
252+
def draw_title_block(self, text: str, x: float, y: float, width: float, title_height: float = 1.0, graphical_scale_length: float = 1000.0, origin: str = "", area: str = ""):
164253
x = x * self.scale
165254
y = y * self.scale
166255
title_height = title_height * self.scale
167256
width = width * self.scale
257+
graphical_scale_length = graphical_scale_length * self.scale
168258

169-
# Add title block text with underline
170-
title_mtext = self.msp.add_mtext(
259+
block = self.doc.blocks.new(name='TITLE_BLOCK')
260+
title_mtext = block.add_mtext(
171261
text=f"{MTextEditor.UNDERLINE_START}{text}{MTextEditor.UNDERLINE_STOP}",
172-
dxfattribs={'layer': 'TITLE_BLOCK', 'style': 'SURVEY_TEXT'},
262+
dxfattribs={'style': 'SURVEY_TEXT'},
173263
)
174-
title_mtext.set_location((x, y))
175264
title_mtext.dxf.attachment_point = ezdxf.enums.MTextEntityAlignment.TOP_CENTER
176265
title_mtext.dxf.char_height = title_height
177266
title_mtext.dxf.width = width
178267

268+
# add block to modelspace
269+
title_ref = self.msp.add_blockref(
270+
'TITLE_BLOCK',
271+
(x, y),
272+
dxfattribs={'layer': 'TITLE_BLOCK'}
273+
)
274+
275+
title_box = bbox.extents(title_ref.virtual_entities())
276+
title_min_y = title_box.extmin.y
277+
title_min_x = title_box.extmin.x
278+
title_max_x = title_box.extmax.x
279+
280+
title_length = title_max_x - title_min_x
281+
graphical_x = title_min_x + ((title_length / 2) - (graphical_scale_length / 2))
282+
283+
# add graphical scale below title
284+
graphical_ref = self.msp.add_blockref(
285+
'GRAPHICAL_SCALE',
286+
(graphical_x, title_min_y - (3.5 * self.scale)),
287+
dxfattribs={'layer': 'TITLE_BLOCK'}
288+
)
289+
290+
graphical_box = bbox.extents(graphical_ref.virtual_entities())
291+
graphical_min_y = graphical_box.extmin.y
292+
293+
origin_mtext = self.msp.add_mtext(
294+
text=f"{MTextEditor.UNDERLINE_START}\C5;{area}{MTextEditor.NEW_LINE}\C1;{origin}{MTextEditor.UNDERLINE_STOP}",
295+
dxfattribs={'style': 'SURVEY_TEXT'},
296+
)
297+
origin_mtext.dxf.attachment_point = ezdxf.enums.MTextEntityAlignment.TOP_CENTER
298+
origin_mtext.dxf.char_height = title_height
299+
origin_mtext.dxf.width = width
300+
origin_mtext.set_location((x, graphical_min_y - (1 * self.scale)))
301+
302+
179303
def draw_frame(self, min_x, min_y, max_x, max_y):
180304
min_x = min_x * self.scale
181305
min_y = min_y * self.scale

models/plan.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,3 @@ def build_title(self) -> str:
164164
soup.append(p4)
165165

166166
return soup.prettify()
167-

0 commit comments

Comments
 (0)