1717
1818
1919class SurveyDXFManager :
20- def __init__ (self , plan_name : str = "Survey Plan" , scale : float = 1.0 ):
20+ def __init__ (self , plan_name : str = "Survey Plan" , scale : float = 1.0 , dxf_version = "R2010" ):
2121 self .plan_name = plan_name
2222 self .scale = scale
23- self .doc = ezdxf .new (dxfversion = "R2010" )
23+ self .doc = ezdxf .new (dxfversion = dxf_version )
2424 self .msp = self .doc .modelspace ()
25- self ._setup_layers ()
25+ self .setup_layers ()
2626
2727
2828 # set units
@@ -33,16 +33,20 @@ def __init__(self, plan_name: str = "Survey Plan", scale: float = 1.0):
3333 self .doc .header ["$AUPREC" ] = 3 # 0d00'00"
3434 self .doc .header ["$ANGBASE" ] = 90.0 # set 0° direction to North
3535
36- def _setup_layers (self ):
37- """Setup standard survey layers"""
36+ def setup_layers (self ):
3837 self .doc .layers .add (name = "BEACONS" , color = colors .BLACK )
39- self .doc .layers .add (name = "PARCELS" , color = colors .RED )
4038 self .doc .layers .add (name = "LABELS" , color = colors .BLACK )
4139 self .doc .layers .add (name = "FRAME" , color = colors .BLACK )
4240 self .doc .layers .add (name = "TITLE_BLOCK" , color = colors .BLACK )
4341 self .doc .layers .add (name = "FOOTER" , color = colors .BLACK )
42+
43+ def setup_cadastral_layers (self ):
44+ self .doc .layers .add (name = "PARCELS" , color = colors .RED )
45+
46+ def setup_topographic_layers (self ):
4447 self .doc .layers .add (name = "BOUNDARY" , color = colors .RED )
45- self .doc .layers .add ('CONTOUR_MAJOR' , true_color = ezdxf .colors .rgb2int ((127 , 31 , 0 )), linetype = "Continuous" , lineweight = 35 )
48+ self .doc .layers .add ('CONTOUR_MAJOR' , true_color = ezdxf .colors .rgb2int ((127 , 31 , 0 )), linetype = "Continuous" ,
49+ lineweight = 35 )
4650 self .doc .layers .add ('CONTOUR_MINOR' , true_color = ezdxf .colors .rgb2int ((127 , 31 , 0 )), linetype = "Continuous" ,
4751 lineweight = 18 )
4852 self .doc .layers .add ('CONTOUR_LABELS' , true_color = ezdxf .colors .rgb2int ((127 , 31 , 0 )))
@@ -53,6 +57,21 @@ def _setup_layers(self):
5357 self .doc .layers .add ('SPOT_HEIGHTS' , true_color = ezdxf .colors .rgb2int ((205 , 105 , 40 )), linetype = "Continuous" ,
5458 lineweight = 25 )
5559
60+ def setup_layout_layers (self ):
61+ self .doc .layers .add (name = "BOUNDARY" , color = colors .RED , linetype = "CONTINUOUS" , lineweight = 50 )
62+ self .doc .layers .add (name = "PARCELS" , color = colors .GREEN , linetype = "CONTINUOUS" , lineweight = 25 )
63+ self .doc .layers .add (name = "ROADS" , color = colors .BLACK , linetype = "CONTINUOUS" , lineweight = 35 )
64+ self .doc .layers .add (name = "ROADS_CL" , color = colors .CYAN , linetype = "DASHDOT" , lineweight = 18 )
65+ self .doc .layers .add (name = "SETBACKS" , color = colors .MAGENTA , linetype = "DASHED" , lineweight = 18 )
66+ self .doc .layers .add (name = "DIMENSIONS" , color = colors .YELLOW , linetype = "CONTINUOUS" , lineweight = 18 )
67+ self .doc .layers .add (name = "TEXT" , color = colors .BLACK , linetype = "CONTINUOUS" , lineweight = 18 )
68+ self .doc .layers .add (name = "GREEN_SPACE" , color = colors .GREEN , linetype = "CONTINUOUS" , lineweight = 25 )
69+ self .doc .layers .add (name = "UTILITIES" , color = colors .BLUE , linetype = "DASHED" , lineweight = 18 )
70+ self .doc .layers .add (name = "EASEMENTS" , true_color = ezdxf .colors .rgb2int ((255 , 165 , 0 )), linetype = "DASHDOT" ,
71+ lineweight = 18 )
72+ self .doc .layers .add (name = "BUILDABLE" , color = colors .GRAY , linetype = "DASHDOT" , lineweight = 18 )
73+
74+
5675 def setup_beacon_style (self , type_ : str = "box" , size : float = 1.0 ):
5776 size = size * self .scale
5877
@@ -156,15 +175,54 @@ def add_parcel(self, parcel_id: str, points: List[Tuple[float, float]], label_si
156175 # )
157176
158177 def add_boundary (self , points : List [Tuple [float , float ]]):
159- """Add a boundaty given its ID and list of (x, y) points"""
178+ """Add a boundary given its ID and list of (x, y) points"""
160179 # scale points
161180 points = [(x * self .scale , y * self .scale ) for x , y , * rest in points ]
162181
163182 self .msp .add_lwpolyline (points , close = True , dxfattribs = {
164183 'layer' : 'BOUNDARY'
165184 })
166185
167- def add_text (self , text : str , x : float , y : float , angle : float = 0.0 , height : float = 1.0 ):
186+ def add_buildable (self , points : List [Tuple [float , float ]]):
187+ """Add a boundary given its ID and list of (x, y) points"""
188+ # scale points
189+ points = [(x * self .scale , y * self .scale ) for x , y , * rest in points ]
190+
191+ self .msp .add_lwpolyline (points , close = True , dxfattribs = {
192+ 'layer' : 'BUILDABLE'
193+ })
194+
195+ def add_road_cl (self , points : List [Tuple [float , float ]]):
196+ # scale points
197+ points = [(x * self .scale , y * self .scale ) for x , y , * rest in points ]
198+
199+ self .msp .add_lwpolyline (points , dxfattribs = {
200+ 'layer' : 'ROAD_CL'
201+ })
202+
203+ def add_road (self , points : List [Tuple [float , float ]]):
204+ # scale points
205+ points = [(x * self .scale , y * self .scale ) for x , y , * rest in points ]
206+
207+ self .msp .add_lwpolyline (points , dxfattribs = {
208+ 'layer' : 'ROADS'
209+ })
210+
211+ def add_greenspace (self , points : List [Tuple [float , float ]], coords ):
212+ """Add a boundary given its ID and list of (x, y) points"""
213+ # scale points
214+ points = [(x * self .scale , y * self .scale ) for x , y , * rest in points ]
215+
216+ self .msp .add_lwpolyline (points , close = True , dxfattribs = {
217+ 'layer' : 'GREEN_SPACE'
218+ })
219+
220+ hatch = self .msp .add_hatch (dxfattribs = {'layer' : 'GREEN_SPACE' })
221+ hatch .set_pattern_fill ('ANSI31' , scale = 0.5 )
222+ hatch .paths .add_polyline_path (coords , is_closed = True )
223+
224+
225+ def add_label (self , text : str , x : float , y : float , angle : float = 0.0 , height : float = 1.0 ):
168226 x = x * self .scale
169227 y = y * self .scale
170228 height = height * self .scale
@@ -183,6 +241,23 @@ def add_text(self, text: str, x: float, y: float, angle: float = 0.0, height: fl
183241 align = TextEntityAlignment .MIDDLE_CENTER
184242 )
185243
244+ def add_text (self , text : str , x : float , y : float , height : float = 1.0 ):
245+ x = x * self .scale
246+ y = y * self .scale
247+ height = height * self .scale
248+
249+ """Add arbitrary text at given coordinates with optional rotation"""
250+ text = self .msp .add_text (
251+ text ,
252+ dxfattribs = {
253+ 'layer' : 'TEXT' ,
254+ 'height' : height ,
255+ 'style' : 'STANDARD'
256+ }
257+ ).set_placement (
258+ (x , y ),
259+ )
260+
186261 def draw_north_arrow (self , x : float , y : float , height : float = 100.0 ):
187262 height = height * self .scale
188263 x = x * self .scale
@@ -250,7 +325,7 @@ def add_north_arrow_label(self, start: Tuple[float, float], stop: Tuple[float, f
250325 'rotation' : angle
251326 }
252327 ).set_placement (
253- (x , y ),
328+ (x + 1 , y + 1 ),
254329 align = TextEntityAlignment .TOP_LEFT ,
255330 )
256331
@@ -400,7 +475,7 @@ def draw_title_block(self, text: str, x: float, y: float, width: float, title_he
400475 graphical_min_y = graphical_box .extmin .y
401476
402477 origin_mtext = self .msp .add_mtext (
403- text = f"{ MTextEditor .UNDERLINE_START } \C5 ;{ area } { MTextEditor .NEW_LINE } \C1 ;{ origin } { MTextEditor .UNDERLINE_STOP } " ,
478+ text = f"{ MTextEditor .UNDERLINE_START } \C1 ;{ area } { MTextEditor .NEW_LINE } \C5 ;{ origin } { MTextEditor .UNDERLINE_STOP } " ,
404479 dxfattribs = {'style' : 'SURVEY_TEXT' },
405480 )
406481 origin_mtext .dxf .attachment_point = ezdxf .enums .MTextEntityAlignment .TOP_CENTER
@@ -630,6 +705,29 @@ def save_dwg(self, dxf_filepath: str, filepath: str = None):
630705 filepath = f"{ self .get_filename ()} .dwg"
631706 odafc .convert (dxf_filepath , filepath )
632707
708+ # def save(self, paper_size: str = "A4", orientation: str = "portrait"):
709+ # # with tempfile.TemporaryDirectory() as tmpdir:
710+ # filename = self.get_filename()
711+ # dxf_path = os.path.join("", f"{filename}.dxf")
712+ # dwg_path = os.path.join("", f"{filename}.dwg")
713+ # pdf_path = os.path.join("", f"{filename}.pdf")
714+ # zip_path = os.path.join("", f"{filename}.zip")
715+ #
716+ # self.save_dxf(dxf_path)
717+ # self.save_dwg(dxf_path, dwg_path)
718+ # self.save_pdf(pdf_path, paper_size=paper_size, orientation=orientation)
719+ #
720+ # # Create a ZIP file containing all three formats
721+ # with zipfile.ZipFile(zip_path, "w") as zipf:
722+ # zipf.write(dxf_path, os.path.basename(dxf_path))
723+ # zipf.write(dwg_path, os.path.basename(dwg_path))
724+ # zipf.write(pdf_path, os.path.basename(pdf_path))
725+ #
726+ # # url = upload_file(zip_path, folder="survey_plans", file_name=filename)
727+ # # if url is None:
728+ # # raise Exception("Upload failed")
729+ # return "url"
730+
633731 def save (self , paper_size : str = "A4" , orientation : str = "portrait" ):
634732 with tempfile .TemporaryDirectory () as tmpdir :
635733 filename = self .get_filename ()
0 commit comments