MeasurementTesting / deploying_3_3.py
Marthee's picture
Update deploying_3_3.py
4edd254 verified
# -*- coding: utf-8 -*-
"""Deploying 3.3
Automatically generated by Colab.
Original file is located at
https://colab.research.google.com/drive/1HEw0DdXhDcxtJN1pjs7bCnlhr-wXX3-m
"""
# pip install pymupdf
# pip install ezdxf
def normalize_vertices(vertices):
"""Sort vertices to ensure consistent order."""
return tuple(sorted(tuple(v) for v in vertices))
def areas_are_similar(area1, area2, tolerance=0.2):
"""Check if two areas are within a given tolerance."""
return abs(area1 - area2) <= tolerance
from ctypes import sizeof
# -*- coding: utf-8 -*-wj
"""Version to be deployed of 3.2 Calculating area/perimeter
Automatically generated by Colab.
Original file is located at
https://colab.research.google.com/drive/1XPeCoTBgWSNBYZ3aMKBteP4YG3w4bORs
"""
# pip install ezdxf[draw]
# pip install --upgrade ezdxf
# pip install pymupdf #==1.22.5
# pip install PyPDF2
# pip install ezdxf scipy
"""## Imports"""
import xml.etree.ElementTree as ET
from PyPDF2 import PdfReader, PdfWriter
from PyPDF2.generic import TextStringObject, NameObject, ArrayObject, FloatObject
from PyPDF2.generic import NameObject, TextStringObject, DictionaryObject, FloatObject, ArrayObject
from typing import NewType
from ctypes import sizeof
import numpy as np
import cv2
from matplotlib import pyplot as plt
import math
from PIL import Image , ImageDraw, ImageFont , ImageColor
import fitz
import ezdxf as ez
import sys
from ezdxf import units
# from google.colab.patches import cv2_imshow
from ezdxf.math import OCS, Matrix44, Vec3
import ezdxf
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from shapely.geometry import Point, Polygon as ShapelyPolygon
from ezdxf.math import Vec2
import random
import pandas as pd
# import google_sheet_Legend
import tsadropboxretrieval
from ezdxf import bbox
from math import sin, cos, radians
import google_sheet_Legend
from PyPDF2 import PdfReader
from io import BytesIO
"""## Notes"""
#new approach to get width and height of dxf plan
'''
This portion is used to convert vertices read from dxf to pixels in order to accurately locate shapes in the image and pdf
ratio :
MeasuredMetric* PixelValue/ DxfMetric = MeasuredPixel
PixelValue: get from pixel conversion code , second number in the bracker represents the perimeter
DxfMetric: measured perimeter from foxit
divide pixelvalue by dxfmetric, will give u a ratio , this is ur dxfratio
'''
"""PDF to image"""
def pdftoimg(datadoc):
doc = fitz.open('pdf',datadoc)
page=doc[0]
pix = page.get_pixmap() # render page to an image
pl=Image.frombytes('RGB', [pix.width,pix.height],pix.samples)
img=np.array(pl)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
return img
# Standard ISO paper sizes in inches
ISO_SIZES_INCHES = {
"A0": (33.11, 46.81),
"A1": (23.39, 33.11),
"A2": (16.54, 23.39),
"A3": (11.69, 16.54),
"A4": (8.27, 11.69),
"A5": (5.83, 8.27),
"A6": (4.13, 5.83),
"A7": (2.91, 4.13),
"A8": (2.05, 2.91),
"A9": (1.46, 2.05),
"A10": (1.02, 1.46)
}
def get_paper_size_in_inches(width, height):
"""Find the closest matching paper size in inches."""
for size, (w, h) in ISO_SIZES_INCHES.items():
if (abs(w - width) < 0.1 and abs(h - height) < 0.1) or (abs(w - height) < 0.1 and abs(h - width) < 0.1):
return size
return "Unknown Size"
def analyze_pdf(datadoc):
# Open the PDF file
pdf_document = fitz.open('pdf',datadoc)
# Iterate through pages and print their sizes
for page_number in range(len(pdf_document)):
page = pdf_document[page_number]
rect = page.rect
width_points, height_points = rect.width, rect.height
# Convert points to inches
width_inches, height_inches = width_points / 72, height_points / 72
paper_size = get_paper_size_in_inches(width_inches, height_inches)
print(f"Page {page_number + 1}: {width_inches:.2f} x {height_inches:.2f} inches ({paper_size})")
pdf_document.close()
return width_inches , height_inches , paper_size
def get_dxfSize(dxfpath):
doc = ezdxf.readfile(dxfpath)
msp = doc.modelspace()
# Create a cache for bounding box calculations
# Get the overall bounding box for all entities in the modelspace
cache = bbox.Cache()
overall_bbox = bbox.extents(msp, cache=cache)
print("Overall Bounding Box:", overall_bbox)
print(overall_bbox.extmin[0]+overall_bbox.extmax[0], overall_bbox.extmin[1]+overall_bbox.extmax[1])
return overall_bbox.extmin[0]+overall_bbox.extmax[0], overall_bbox.extmin[1]+overall_bbox.extmax[1]
def switch_case(argument):
switcher = {
"A0": 1.27,
"A1": 2.54,
"A2": 5.08,
"A3": 10.16,
"A4": 20.32,
"A5": 40.64,
"A6": 81.28,
"A7": 162.56,
"A8": 325.12,
"A9": 650.24,
"A10": 1300.48
}
# Get the value from the dictionary; if not found, return a default value
print("Final Ratio=",switcher.get(argument, 1))
return switcher.get(argument, 1)
def RetriveRatio(datadoc,dxfpath):
width,height,paper_size = analyze_pdf (datadoc)
if(width > height ):
bigger=width
else:
bigger=height
width_dxf,height_dxf = get_dxfSize(dxfpath)
if(width_dxf > height_dxf ):
bigger_dxf=width_dxf
else:
bigger_dxf=height_dxf
if(0.2 < bigger_dxf/bigger < 1.2):
print("bigger_dxf/bigger",bigger/bigger_dxf)
argument = paper_size
FinalRatio=switch_case(argument)
else:
FinalRatio=1
return FinalRatio
"""Flips image
DXF origin is at the bottom left while img origin is top left
"""
def flip(img):
height, width = img.shape[:2]
# Define the rotation angle (clockwise)
angle = 180
# Calculate the rotation matrix
rotation_matrix = cv2.getRotationMatrix2D((width/2, height/2), angle, 1)
# Rotate the image
rotated_image = cv2.warpAffine(img, rotation_matrix, (width, height))
flipped_horizontal = cv2.flip(rotated_image, 1)
return flipped_horizontal
def aci_to_rgb(aci):
aci_rgb_map = {
0: (0, 0, 0),
1: (255, 0, 0),
2: (255, 255, 0),
3: (0, 255, 0),
4: (0, 255, 255),
5: (0, 0, 255),
6: (255, 0, 255),
7: (255, 255, 255),
8: (65, 65, 65),
9: (128, 128, 128),
10: (255, 0, 0),
11: (255, 170, 170),
12: (189, 0, 0),
13: (189, 126, 126),
14: (129, 0, 0),
15: (129, 86, 86),
16: (104, 0, 0),
17: (104, 69, 69),
18: (79, 0, 0),
19: (79, 53, 53),
20: (255, 63, 0),
21: (255, 191, 170),
22: (189, 46, 0),
23: (189, 141, 126),
24: (129, 31, 0),
25: (129, 96, 86),
26: (104, 25, 0),
27: (104, 78, 69),
28: (79, 19, 0),
29: (79, 59, 53),
30: (255, 127, 0),
31: (255, 212, 170),
32: (189, 94, 0),
33: (189, 157, 126),
34: (129, 64, 0),
35: (129, 107, 86),
36: (104, 52, 0),
37: (104, 86, 69),
38: (79, 39, 0),
39: (79, 66, 53),
40: (255, 191, 0),
41: (255, 234, 170),
42: (189, 141, 0),
43: (189, 173, 126),
44: (129, 96, 0),
45: (129, 118, 86),
46: (104, 78, 0),
47: (104, 95, 69),
48: (79, 59, 0),
49: (79, 73, 53),
50: (255, 255, 0),
51: (255, 255, 170),
52: (189, 189, 0),
53: (189, 189, 126),
54: (129, 129, 0),
55: (129, 129, 86),
56: (104, 104, 0),
57: (104, 104, 69),
58: (79, 79, 0),
59: (79, 79, 53),
60: (191, 255, 0),
61: (234, 255, 170),
62: (141, 189, 0),
63: (173, 189, 126),
64: (96, 129, 0),
65: (118, 129, 86),
66: (78, 104, 0),
67: (95, 104, 69),
68: (59, 79, 0),
69: (73, 79, 53),
70: (127, 255, 0),
71: (212, 255, 170),
72: (94, 189, 0),
73: (157, 189, 126),
74: (64, 129, 0),
75: (107, 129, 86),
76: (52, 104, 0),
77: (86, 104, 69),
78: (39, 79, 0),
79: (66, 79, 53),
80: (63, 255, 0),
81: (191, 255, 170),
82: (46, 189, 0),
83: (141, 189, 126),
84: (31, 129, 0),
85: (96, 129, 86),
86: (25, 104, 0),
87: (78, 104, 69),
88: (19, 79, 0),
89: (59, 79, 53),
90: (0, 255, 0),
91: (170, 255, 170),
92: (0, 189, 0),
93: (126, 189, 126),
94: (0, 129, 0),
95: (86, 129, 86),
96: (0, 104, 0),
97: (69, 104, 69),
98: (0, 79, 0),
99: (53, 79, 53),
100: (0, 255, 63),
101: (170, 255, 191),
102: (0, 189, 46),
103: (126, 189, 141),
104: (0, 129, 31),
105: (86, 129, 96),
106: (0, 104, 25),
107: (69, 104, 78),
108: (0, 79, 19),
109: (53, 79, 59),
110: (0, 255, 127),
111: (170, 255, 212),
112: (0, 189, 94),
113: (126, 189, 157),
114: (0, 129, 64),
115: (86, 129, 107),
116: (0, 104, 52),
117: (69, 104, 86),
118: (0, 79, 39),
119: (53, 79, 66),
120: (0, 255, 191),
121: (170, 255, 234),
122: (0, 189, 141),
123: (126, 189, 173),
124: (0, 129, 96),
125: (86, 129, 118),
126: (0, 104, 78),
127: (69, 104, 95),
128: (0, 79, 59),
129: (53, 79, 73),
130: (0, 255, 255),
131: (170, 255, 255),
132: (0, 189, 189),
133: (126, 189, 189),
134: (0, 129, 129),
135: (86, 129, 129),
136: (0, 104, 104),
137: (69, 104, 104),
138: (0, 79, 79),
139: (53, 79, 79),
140: (0, 191, 255),
141: (170, 234, 255),
142: (0, 141, 189),
143: (126, 173, 189),
144: (0, 96, 129),
145: (86, 118, 129),
146: (0, 78, 104),
147: (69, 95, 104),
148: (0, 59, 79),
149: (53, 73, 79),
150: (0, 127, 255),
151: (170, 212, 255),
152: (0, 94, 189),
153: (126, 157, 189),
154: (0, 64, 129),
155: (86, 107, 129),
156: (0, 52, 104),
157: (69, 86, 104),
158: (0, 39, 79),
159: (53, 66, 79),
160: (0, 63, 255),
161: (170, 191, 255),
162: (0, 46, 189),
163: (126, 141, 189),
164: (0, 31, 129),
165: (86, 96, 129),
166: (0, 25, 104),
167: (69, 78, 104),
168: (0, 19, 79),
169: (53, 59, 79),
170: (0, 0, 255),
171: (170, 170, 255),
172: (0, 0, 189),
173: (126, 126, 189),
174: (0, 0, 129),
175: (86, 86, 129),
176: (0, 0, 104),
177: (69, 69, 104),
178: (0, 0, 79),
179: (53, 53, 79),
180: (63, 0, 255),
181: (191, 170, 255),
182: (46, 0, 189),
183: (141, 126, 189),
184: (31, 0, 129),
185: (96, 86, 129),
186: (25, 0, 104),
187: (78, 69, 104),
188: (19, 0, 79),
189: (59, 53, 79),
190: (127, 0, 255),
191: (212, 170, 255),
192: (94, 0, 189),
193: (157, 126, 189),
194: (64, 0, 129),
195: (107, 86, 129),
196: (52, 0, 104),
197: (86, 69, 104),
198: (39, 0, 79),
199: (66, 53, 79),
200: (191, 0, 255),
201: (234, 170, 255),
202: (141, 0, 189),
203: (173, 126, 189),
204: (96, 0, 129),
205: (118, 86, 129),
206: (78, 0, 104),
207: (95, 69, 104),
208: (59, 0, 79),
209: (73, 53, 79),
210: (255, 0, 255),
211: (255, 170, 255),
212: (189, 0, 189),
213: (189, 126, 189),
214: (129, 0, 129),
215: (129, 86, 129),
216: (104, 0, 104),
217: (104, 69, 104),
218: (79, 0, 79),
219: (79, 53, 79),
220: (255, 0, 191),
221: (255, 170, 234),
222: (189, 0, 141),
223: (189, 126, 173),
224: (129, 0, 96),
225: (129, 86, 118),
226: (104, 0, 78),
227: (104, 69, 95),
228: (79, 0, 59),
229: (79, 53, 73),
230: (255, 0, 127),
231: (255, 170, 212),
232: (189, 0, 94),
233: (189, 126, 157),
234: (129, 0, 64),
235: (129, 86, 107),
236: (104, 0, 52),
237: (104, 69, 86),
238: (79, 0, 39),
239: (79, 53, 66),
240: (255, 0, 63),
241: (255, 170, 191),
242: (189, 0, 46),
243: (189, 126, 141),
244: (129, 0, 31),
245: (129, 86, 96),
246: (104, 0, 25),
247: (104, 69, 78),
248: (79, 0, 19),
249: (79, 53, 59),
250: (51, 51, 51),
251: (80, 80, 80),
252: (105, 105, 105),
253: (130, 130, 130),
254: (190, 190, 190),
255: (255, 255, 255)
}
# Default to white if index is invalid or not found
return aci_rgb_map.get(aci, (255, 255, 255))
def int_to_rgb(color_int):
"""Convert an integer to an (R, G, B) tuple."""
r = (color_int >> 16) & 255
g = (color_int >> 8) & 255
b = color_int & 255
return (r, g, b)
def get_hatch_color(entity):
# Check if the entity has a "true color" set
if entity.dxf.hasattr('true_color'):
true_color = entity.dxf.true_color
rgb_color = int_to_rgb(true_color) # Convert integer to (R, G, B)
print(f"True color detected (RGB): {rgb_color}")
return rgb_color
color_index = entity.dxf.color
print("color_index = ", color_index)
# Check if the color is set to ByLayer or ByBlock
if color_index == 0: # ByLayer color
print("Color is ByLayer, checking layer color...")
layer_name = entity.dxf.layer
layer = entity.doc.layers.get(layer_name)
if layer: # Ensure layer exists
layer_color_index = layer.dxf.color
print(f"Layer '{layer_name}' Color Index = {layer_color_index}")
return aci_to_rgb(layer_color_index) # Use custom aci_to_rgb function
else:
print(f"Layer '{layer_name}' not found, defaulting to white.")
return (255, 255, 255) # Default to white if layer not found
elif color_index == 256: # ByBlock color
print("Color is ByBlock, checking block color or defaulting to white.")
block_color = (255, 255, 255) # White as default
# Check if the entity is inside a block reference and inherit its color
if hasattr(entity, 'block'): # Check if the entity belongs to a block
block_ref = entity.block
if block_ref.dxf.hasattr('color'):
block_color = aci_to_rgb(block_ref.dxf.color)
print(f"Block reference color found: {block_color}")
else:
print("Block has no color attribute, using default (white).")
return block_color
# Otherwise, convert the ACI color to RGB
print(f"Entity Color Index = {color_index}")
if 1 <= color_index <= 255:
rgb_color = aci_to_rgb(color_index) # Use custom aci_to_rgb function
print(f"Converted RGB = {rgb_color}")
return rgb_color
# Default to white if color index is out of bounds or invalid
print("Invalid or unhandled color index, defaulting to white.")
return (255, 255, 255)
"""### Hatched areas"""
def get_hatched_areas(datadoc,filename,FinalRatio,rotationangle):
text_with_positions = []
text_color_mapping = {}
color_palette = [
(255, 0, 0), (0, 0, 255), (0, 255, 255), (0, 64, 0), (255, 204, 0),
(255, 128, 64), (255, 0, 128), (255, 128, 192), (128, 128, 255),
(128, 64, 0), (0, 255, 0), (0, 200, 0), (255, 128, 255), (128, 0, 255),
(0, 128, 192), (128, 0, 128), (128, 0, 0), (0, 128, 255), (149, 1, 70),
(255, 182, 128), (222, 48, 71), (240, 0, 112), (255, 0, 255),
(192, 46, 65), (0, 0, 128), (0, 128, 64), (255, 255, 0), (128, 0, 80),
(255, 255, 128), (90, 255, 140), (255, 200, 20), (91, 16, 51),
(90, 105, 138), (114, 10, 138), (36, 82, 78), (225, 105, 190),
(108, 150, 170), (11, 35, 75), (42, 176, 170), (255, 176, 170),
(209, 151, 15), (81, 27, 85), (226, 106, 122), (67, 119, 149),
(159, 179, 140), (159, 179, 30), (255, 85, 198), (255, 27, 85),
(188, 158, 8), (140, 188, 120), (59, 61, 52), (65, 81, 21),
(212, 255, 174), (15, 164, 90), (41, 217, 245), (213, 23, 182),
(11, 85, 169), (78, 153, 239), (0, 66, 141), (64, 98, 232),
(140, 112, 255), (57, 33, 154), (194, 117, 252), (116, 92, 135),
(74, 43, 98), (188, 13, 123), (129, 58, 91), (255, 128, 100),
(171, 122, 145), (255, 98, 98), (222, 48, 77)
]
doc = ezdxf.readfile(filename)
doc.header['$MEASUREMENT'] = 1
msp = doc.modelspace()
trial=0
hatched_areas = []
threshold=0.01
unique_shapes = []
for entity in doc.modelspace().query('TEXT MTEXT'):
if hasattr(entity, 'text'): # Ensure the entity has text content
text = entity.text
if text.startswith('C') and (len(text) > 1 and (text[1].isdigit() or text[1].upper() == 'T' or text[1].upper() == 'L')):
parts = text.split(' ') # Split into two parts: before and after the first newline
# print("Parts = ",parts[0])
main_text = parts[0] # Text before the first newline
# Check if the main text starts with 'C' followed by a number or 'T'
# if pattern.match(main_text):
position = entity.dxf.insert
# Check if the text already has a color assigned
if main_text not in text_color_mapping:
# Assign a new color from the palette
color_index = len(text_color_mapping) % len(color_palette)
text_color_mapping[main_text] = color_palette[color_index]
# Get the assigned color
color = text_color_mapping[main_text]
# Set the entity's true color
# entity.dxf.true_color = rgb_to_true_color(color)
# Append text, position, and color to the array
text_with_positions.append([main_text, position, color])
for entity in msp:
if entity.dxftype() == 'HATCH':
# print(f"Processing HATCH entity: {entity}")
for path in entity.paths:
vertices = [] # Reset vertices for each path
if str(path.type) == 'BoundaryPathType.POLYLINE':
# Handle POLYLINE type HATCH
vertices = [(vertex[0] * FinalRatio, vertex[1] * FinalRatio) for vertex in path.vertices]
if len(vertices) > 3:
poly = ShapelyPolygon(vertices)
minx, miny, maxx, maxy = poly.bounds
width = maxx - minx
height = maxy - miny
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
area1 = round(poly.area, 3)
perimeter = round(poly.length, 3)
normalized_vertices = normalize_vertices(vertices)
rgb_color = get_hatch_color(entity)
if(rgb_color == (255, 255, 255)):
if(len(text_with_positions)>0):
for text, position, color in text_with_positions:
text_position = Point(position[0], position[1])
if poly.contains(text_position):
rgb_color = color
break
duplicate_found = False
for existing_vertices, existing_area in unique_shapes:
if normalized_vertices == existing_vertices and areas_are_similar(area1, existing_area):
duplicate_found = True
break
if not duplicate_found:
# rgb_color = get_hatch_color(entity) # Assuming this function exists
unique_shapes.append((normalized_vertices, area1))
hatched_areas.append([vertices, area1, perimeter, rgb_color])
elif str(path.type) == 'BoundaryPathType.EDGE':
# Handle EDGE type HATCH
vert = []
for edge in path.edges:
x, y = edge.start
x1, y1 = edge.end
vert.append((x * FinalRatio, y * FinalRatio))
vert.append((x1 * FinalRatio, y1 * FinalRatio))
poly = ShapelyPolygon(vert)
minx, miny, maxx, maxy = poly.bounds
width = maxx - minx
height = maxy - miny
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
area1 = round(poly.area, 3)
perimeter = round(poly.length, 3)
normalized_vertices = normalize_vertices(vert)
rgb_color = get_hatch_color(entity)
if(rgb_color == (255, 255, 255)):
if(len(text_with_positions)>0):
for text, position, color in text_with_positions:
text_position = Point(position[0], position[1])
if poly.contains(text_position):
rgb_color = color
break
duplicate_found = False
for existing_vertices, existing_area in unique_shapes:
if normalized_vertices == existing_vertices and areas_are_similar(area1, existing_area):
duplicate_found = True
break
if not duplicate_found:
# rgb_color = get_hatch_color(entity) # Assuming this function exists
unique_shapes.append((normalized_vertices, area1))
hatched_areas.append([vert, area1, perimeter, rgb_color])
else:
print(f"Unhandled path type: {path.type}")
elif entity.dxftype() == 'SOLID':
vertices = [entity.dxf.vtx0 * (FinalRatio), entity.dxf.vtx1* (FinalRatio), entity.dxf.vtx2* (FinalRatio), entity.dxf.vtx3* (FinalRatio)]
poly = ShapelyPolygon(vertices)
minx, miny, maxx, maxy = poly.bounds
# Calculate the width and height of the bounding box
width = maxx - minx
height = maxy - miny
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
area1 = round(poly.area, 3)
perimeter = round(poly.length, 3)
normalized_vertices = normalize_vertices(vertices)
duplicate_found = False
for existing_vertices, existing_area in unique_shapes:
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
duplicate_found = True
break
if not duplicate_found:
rgb_color = get_hatch_color(entity) # Assuming this function exists
unique_shapes.append((normalized_vertices, area1))
hatched_areas.append([vertices, area1, perimeter, rgb_color])
elif entity.dxftype() == 'LWPOLYLINE':
vertices = []
lwpolyline = entity
points = lwpolyline.get_points()
flag = 0
# Collect vertices and apply the FinalRatio
for i in range(len(points)):
vertices.append([points[i][0] * FinalRatio, points[i][1] * FinalRatio])
# # Ensure there are more than 3 vertices
if len(vertices) > 3:
# Check if the polyline is closed
if vertices[0][0] == vertices[-1][0] or vertices[0][1] == vertices[-1][1]:
poly = ShapelyPolygon(vertices)
minx, miny, maxx, maxy = poly.bounds
# Calculate width and height of the bounding box
width = maxx - minx
height = maxy - miny
# Check area and size constraints
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
area1 = round(poly.area, 3)
perimeter = round(poly.length, 3)
normalized_vertices = normalize_vertices(vertices)
duplicate_found = False
for existing_vertices, existing_area in unique_shapes:
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
duplicate_found = True
break
if not duplicate_found:
rgb_color = get_hatch_color(entity) # Assuming this function exists
unique_shapes.append((normalized_vertices, area1))
hatched_areas.append([vertices, area1, perimeter, rgb_color])
elif entity.dxftype() == 'POLYLINE':
# print("In POLYLINE")
flag=0
vertices = [(v.dxf.location.x * (FinalRatio), v.dxf.location.y * (FinalRatio)) for v in entity.vertices]
# print('Vertices:', vertices)
if(len(vertices)>3):
if(vertices[0][0] == vertices[len(vertices)-1][0] or vertices[0][1] == vertices[len(vertices)-1][1]):
poly=ShapelyPolygon(vertices)
minx, miny, maxx, maxy = poly.bounds
# Calculate the width and height of the bounding box
width = maxx - minx
height = maxy - miny
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
area1 = round(poly.area,3)
perimeter = round (poly.length,3)
normalized_vertices = normalize_vertices(vertices)
duplicate_found = False
for existing_vertices, existing_area in unique_shapes:
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
duplicate_found = True
break
if not duplicate_found:
rgb_color = get_hatch_color(entity) # Assuming this function exists
unique_shapes.append((normalized_vertices, area1))
hatched_areas.append([vertices, area1, perimeter, rgb_color])
elif entity.dxftype() == 'SPLINE':
spline_entity = entity
vertices = []
control_points = spline_entity.control_points
if(len(control_points)>3):
for i in range(len(control_points)):
vertices.append([control_points[i][0]* (FinalRatio),control_points[i][1]* (FinalRatio)])
poly=ShapelyPolygon(vertices)
minx, miny, maxx, maxy = poly.bounds
# Calculate the width and height of the bounding box
width = maxx - minx
height = maxy - miny
if (poly.area > 0.9 and (height > 0.7 and width > 0.7)):
area1 = round(poly.area,3)
perimeter = round (poly.length,3)
normalized_vertices = normalize_vertices(vertices)
duplicate_found = False
for existing_vertices, existing_area in unique_shapes:
if normalized_vertices == existing_vertices or areas_are_similar(area1, existing_area):
duplicate_found = True
break
if not duplicate_found:
rgb_color = get_hatch_color(entity) # Assuming this function exists
unique_shapes.append((normalized_vertices, area1))
hatched_areas.append([vertices, area1, perimeter, rgb_color])
sorted_data = sorted(hatched_areas, key=lambda x: x[1])
return sorted_data
"""### Rotate polygon"""
def rotate_point(point, angle,pdfrotation,width,height, center_point=(0, 0)):
"""Rotates a point around center_point(origin by default)
Angle is in degrees.
Rotation is counter-clockwise
"""
angle_rad = radians(angle % 360)
# Shift the point so that center_point becomes the origin
new_point = (point[0] - center_point[0], point[1] - center_point[1])
new_point = (new_point[0] * cos(angle_rad) - new_point[1] * sin(angle_rad),
new_point[0] * sin(angle_rad) + new_point[1] * cos(angle_rad))
# Reverse the shifting we have done
if pdfrotation!=0:
new_point = (new_point[0]+width + center_point[0], new_point[1] + center_point[1]) #pdfsize[2] is the same as +width
else:
new_point = (new_point[0] + center_point[0], new_point[1]+ height + center_point[1]) # pdfsize[3] is the same as +height
# new_point = (new_point[0] + center_point[0], new_point[1] + center_point[1])
return new_point
def rotate_polygon(polygon, angle, pdfrotation,width,height,center_point=(0, 0)):
"""Rotates the given polygon which consists of corners represented as (x,y)
around center_point (origin by default)
Rotation is counter-clockwise
Angle is in degrees
"""
rotated_polygon = []
for corner in polygon:
rotated_corner = rotate_point(corner, angle,pdfrotation,width,height, center_point)
rotated_polygon.append(rotated_corner)
return rotated_polygon
#create a dataframe containing color , count(how many times is this object found in the plan), area of 1 of these shapes, total area
#perimeter, totat perimeter, length, total length
#import pandas as pd
#SimilarAreaDictionary= pd.DataFrame(columns=['Guess','Color','Occurences','Area','Total Area','Perimeter','Total Perimeter','Length','Total Length','R','G','B'])
#loop 3la hatched areas and count the occurences of each shape w create a table bl hagat di
def Create_DF(dxfpath,datadoc,hatched_areas):
FinalRatio= RetriveRatio(datadoc,dxfpath)
# hatched_areas = get_hatched_areas(dxfpath,FinalRatio)
# print('hatched_areas',hatched_areas)
# hatched_areas=remove_duplicate_shapes(new_hatched_areas)
# SimilarAreaDictionary= pd.DataFrame(columns=['Area', 'Total Area', 'Perimeter', 'Total Perimeter', 'Occurences', 'Color'])
SimilarAreaDictionary= pd.DataFrame(columns=['Guess','Color','Occurences','Area','Total Area','Perimeter','Total Perimeter','Length','Total Length','Texts','Comments'])
# colorRanges2=generate_color_array(30000)
# colorRanges = [[255, 0, 0], [0, 0, 255], [0, 255, 255], [0, 64, 0], [255, 204, 0], [255, 128, 64], [255, 0, 128], [255, 128, 192], [128, 128, 255], [128, 64, 0],[0, 255, 0],[0, 200, 0],[255, 128, 255], [128, 0, 255], [0, 128, 192], [128, 0, 128],[128, 0, 0], [0, 128, 255], [149, 1, 70], [255, 182, 128], [222, 48, 71], [240, 0, 112], [255, 0, 255], [192, 46, 65], [0, 0, 128],[0, 128, 64],[255, 255, 0], [128, 0, 80], [255, 255, 128], [90, 255, 140],[255, 200, 20],[91, 16, 51], [90, 105, 138], [114, 10, 138], [36, 82, 78], [225, 105, 190], [108, 150, 170], [11, 35, 75], [42, 176, 170], [255, 176, 170], [209, 151, 15],[81, 27, 85], [226, 106, 122], [67, 119, 149], [159, 179, 140], [159, 179, 30],[255, 85, 198], [255, 27, 85], [188, 158, 8],[140, 188, 120], [59, 61, 52], [65, 81, 21], [212, 255, 174], [15, 164, 90],[41, 217, 245], [213, 23, 182], [11, 85, 169], [78, 153, 239], [0, 66, 141],[64, 98, 232], [140, 112, 255], [57, 33, 154], [194, 117, 252], [116, 92, 135], [74, 43, 98], [188, 13, 123], [129, 58, 91], [255, 128, 100], [171, 122, 145], [255, 98, 98], [222, 48, 77]]
# colorUsed=[]
TotalArea=0
TotalPerimeter=0
for shape in hatched_areas:
area = shape[1] # area
perimeter = shape[2] # perimeter
# if(i < len(colorRanges)):
# color = colorRanges[i]
# colorUsed.append(color)
# else:
# color = colorRanges2[i]
# colorUsed.append(color)
TotalArea = area
TotalPerimeter = perimeter
tol=0
condition1 = (SimilarAreaDictionary['Area'] >= area - tol) & (SimilarAreaDictionary['Area'] <= area +tol)
condition2 = (SimilarAreaDictionary['Perimeter'] >= perimeter -tol) & (SimilarAreaDictionary['Perimeter'] <= perimeter +tol)
combined_condition = condition1 & condition2
if any(combined_condition):
index = np.where(combined_condition)[0][0]
SimilarAreaDictionary.at[index, 'Occurences'] += 1
SimilarAreaDictionary.at[index, 'Total Area'] = SimilarAreaDictionary.at[index, 'Total Area'] + area
SimilarAreaDictionary.at[index, 'Total Perimeter'] = SimilarAreaDictionary.at[index, 'Total Perimeter'] + perimeter
else:
TotalArea=area
TotalPerimeter=perimeter
new_data = {'Area': area, 'Total Area': TotalArea ,'Perimeter': perimeter, 'Total Perimeter': TotalPerimeter, 'Occurences': 1, 'Color':shape[3],'Comments':''} #add color here and read color to insert in
SimilarAreaDictionary = pd.concat([SimilarAreaDictionary, pd.DataFrame([new_data])], ignore_index=True)
# print(SimilarAreaDictionary)
return SimilarAreaDictionary
"""### Draw on Image and PDF"""
def adjustannotations(OutputPdfStage1):
input_pdf_path = OutputPdfStage1
output_pdf_path = "AnnotationAdjusted.pdf"
# Load the input PDF
pdf_bytes_io = BytesIO(OutputPdfStage1)
reader = PdfReader(pdf_bytes_io)
writer = PdfWriter()
# Append all pages to the writer
writer.append_pages_from_reader(reader)
# Add metadata (optional)
metadata = reader.metadata
writer.add_metadata(metadata)
# Iterate over pages
for page_index, page in enumerate(writer.pages):
# page.update({
# NameObject("/UserUnit"): FloatObject(1.0), # 1 unit = 1 real-world unit (e.g., 1 meter)
# NameObject("/VP"): ArrayObject([
# DictionaryObject({
# NameObject("/Type"): NameObject("/Viewport"),
# NameObject("/BBox"): ArrayObject([
# FloatObject(0), FloatObject(0), FloatObject(1000), FloatObject(1000)
# ]), # Bounding box for the viewport
# NameObject("/Measure"): DictionaryObject({
# NameObject("/Type"): NameObject("/Measure"),
# NameObject("/Subtype"): NameObject("/RL"),
# NameObject("/X"): FloatObject(1),
# NameObject("/Y"): FloatObject(1),
# NameObject("/U"): TextStringObject("m"), # Units (meters)
# }),
# })
# ])
# })
if "/Annots" in page:
annotations = page["/Annots"]
for annot_index, annot in enumerate(annotations):
obj = annot.get_object()
print("obj", obj)
# print(obj.get("/IT"))
if obj.get("/Subtype") == "/Polygon":
print("AWL ANNOT IF")
# Check the /IT value to differentiate annotations
if "/Contents" in obj and "sq m" in obj["/Contents"]:
print("Tany IF")
obj.update({
NameObject("/Measure"): DictionaryObject({
NameObject("/Type"): NameObject("/Measure"),
NameObject("/Area"): DictionaryObject({
NameObject("/G"): FloatObject(1),
NameObject("/U"): TextStringObject("sq m"), # Unit of measurement for area
}),
NameObject("/X"): FloatObject(1), # Horizontal scale (e.g., 1 unit = 1 meter)
NameObject("/Y"): FloatObject(1), # Vertical scale
}),
NameObject("/IT"): NameObject("/Area_Annotation"), # Use more distinctive name
NameObject("/Subj"): TextStringObject("Area Measurement"), # Intent explicitly for Area
})
print("After Update:", obj)
# # Save the modified PDF
output_pdf_io = BytesIO()
writer.write(output_pdf_io)
output_pdf_io.seek(0)
return output_pdf_io.read()
# writer.write(new_bytes_object) # This writes the modified PDF data to new_bytes_object
# new_bytes_object.seek(0)
# return new_bytes_object.read()
# def adjustannotations(OutputPdfStage1):
# """
# Adjusts annotations in the PDF to include measurement and scale information.
# Parameters:
# OutputPdfStage1 (str): Path to the input PDF file.
# Returns:
# bytes: The adjusted PDF data as bytes.
# """
# with open(OutputPdfStage1, "rb") as pdf_file:
# reader = PdfReader(pdf_file)
# writer = PdfWriter()
# # Append all pages from reader to writer
# writer.append_pages_from_reader(reader)
# # Iterate over pages and add measurement details
# for page_index, page in enumerate(writer.pages):
# # Add scale settings at the page level
# # page.update({
# # NameObject("/UserUnit"): FloatObject(1.0), # 1 unit = 1 real-world unit
# # NameObject("/VP"): ArrayObject([
# # DictionaryObject({
# # NameObject("/Type"): NameObject("/Viewport"),
# # NameObject("/BBox"): ArrayObject([
# # FloatObject(0), FloatObject(0), FloatObject(100), FloatObject(100)
# # ]), # Bounding box for the viewport
# # NameObject("/Measure"): DictionaryObject({
# # NameObject("/Type"): NameObject("/Measure"),
# # NameObject("/Subtype"): NameObject("/RL"),
# # NameObject("/X"): FloatObject(1), # Horizontal scale
# # NameObject("/Y"): FloatObject(1), # Vertical scale
# # NameObject("/U"): TextStringObject("m"), # Units (meters)
# # }),
# # })
# # ])
# # })
# # Process annotations
# if "/Annots" in page:
# annotations = page["/Annots"]
# for annot in annotations:
# obj = annot.get_object()
# # Adjust polygon annotations with area measurements
# if obj.get("/Subtype") == "/Polygon" and "/Contents" in obj and "sq m" in obj["/Contents"]:
# obj.update({
# NameObject("/Measure"): DictionaryObject({
# NameObject("/Type"): NameObject("/Measure"),
# NameObject("/Area"): DictionaryObject({
# NameObject("/G"): FloatObject(1),
# NameObject("/U"): TextStringObject("sq m"), # Area unit
# }),
# NameObject("/X"): FloatObject(1), # Horizontal scale
# NameObject("/Y"): FloatObject(1), # Vertical scale
# NameObject("/U"): TextStringObject("m"), # Units (meters)
# }),
# NameObject("/IT"): NameObject("/Area_Annotation"),
# NameObject("/Subj"): TextStringObject("Area Measurement"),
# })
# print(obj)
# output_pdf_io = BytesIO()
# writer.write(output_pdf_io)
# output_pdf_io.seek(0) # Ensure buffer is at the start
# return output_pdf_io
def mainFunctionDrawImgPdf(datadoc,dxfpath, dxfratio,pdfpath=0,pdfname=0):
OutputPdfStage1='BB Trial.pdf'
FinalRatio= RetriveRatio(datadoc,dxfpath)
# hatched_areas = get_hatched_areas(dxfpath,FinalRatio)
# hatched_areas=remove_duplicate_shapes(new_hatched_areas)
img=pdftoimg(datadoc)
flipped_horizontal=flip(img)
allcnts = []
imgg = flipped_horizontal
# imgtransparent1=imgg.copy()
doc = fitz.open('pdf',datadoc)
page2 = doc[0]
rotationOld=page2.rotation
derotationMatrix=page2.derotation_matrix
pix=page2.get_pixmap()
width=abs(page2.mediabox[2])+abs(page2.mediabox[0])
height=abs(page2.mediabox[3])+abs(page2.mediabox[1])
print('mediabox', width , height)
if page2.rotation!=0:
rotationangle = page2.rotation
page2.set_rotation(0)
ratio = pix.width/ img.shape[0]
else:
ratio = pix.width/ img.shape[1]
rotationangle = 270
hatched_areas = get_hatched_areas(datadoc,dxfpath,FinalRatio,rotationangle)
allshapes=[]
# Iterate through each polygon in metric units
NewColors = []
SimilarAreaDictionary=Create_DF(dxfpath,datadoc,hatched_areas)
i=0
flagcolor = 0
ColorCheck=[]
for polygon in hatched_areas:
cntPoints = []
cntPoints1 = []
shapeePerimeter = []
shapeeArea = []
blackImgShapes = np.zeros(imgg.shape[:2], dtype="uint8")
blackImgShapes= cv2.cvtColor(blackImgShapes, cv2.COLOR_GRAY2BGR)
# Convert each vertex from metric to pixel coordinates
for vertex in polygon[0]:
x = (vertex[0]) *dxfratio
y = (vertex[1]) *dxfratio
if rotationangle==0:
if y<0:
y=y*-1
cntPoints.append([int(x), int(y)])
cntPoints1.append([x, y])
cv2.drawContours(blackImgShapes, [np.array(cntPoints)], -1, ([255,255,255]), thickness=-1)
x, y, w, h = cv2.boundingRect(np.array(cntPoints))
firstpoint = 0
for poi in np.array(cntPoints1):
if firstpoint == 0:
x2, y2 = poi
p2 = fitz.Point(x2,y2)
# p1 = fitz.Point(x1,y1)
p2=p2*derotationMatrix
shapeePerimeter.append([p2[0],p2[1]])
firstpoint = 1
else:
x1, y1 = poi
p1 = fitz.Point(x1,y1)
# p1 = fitz.Point(x1,y1)
p1=p1*derotationMatrix
print("P1 = ",p1)
shapeePerimeter.append([p1[0],p1[1]])
shapeePerimeter.append([p2[0],p2[1]])
shapeePerimeter=np.flip(shapeePerimeter,1)
shapeePerimeter=rotate_polygon(shapeePerimeter,rotationangle,rotationOld,width,height)
for poi in np.array(cntPoints1):
x1, y1 = poi
p1 = fitz.Point(x1,y1)
# p1 = fitz.Point(x1,y1)
p1=p1*derotationMatrix
print("P1 = ",p1)
shapeeArea.append([p1[0],p1[1]])
shapeeArea.append([p2[0],p2[1]])
shapeeArea=np.flip(shapeeArea,1)
shapeeArea=rotate_polygon(shapeeArea,rotationangle,rotationOld,width,height)
tol=0
condition1 = (SimilarAreaDictionary['Area'] >= polygon[1] - tol) & (SimilarAreaDictionary['Area'] <= polygon[1] +tol)
condition2 = (SimilarAreaDictionary['Perimeter'] >= polygon[2] -tol) & (SimilarAreaDictionary['Perimeter'] <= polygon[2] +tol)
combined_condition = condition1 & condition2
if any(combined_condition):
flagcolor = 1
index = np.where(combined_condition)[0][0]
# print(SimilarAreaDictionary.at[index, 'Color'])
NewColors=SimilarAreaDictionary.at[index, 'Color']
else:
flagcolor = 2
NewColors=SimilarAreaDictionary.at[i, 'Color']
if(int(NewColors[0])==255 and int(NewColors[1])==255 and int(NewColors[2])==255):
WhiteImgFinal = cv2.bitwise_and(blackImgShapes,imgg)
flipped=flip(WhiteImgFinal)
imgslice = WhiteImgFinal[y:y+h, x:x+w]
if(imgslice.shape[0] != 0 and imgslice.shape[1] != 0):
flippedSlice=flip(imgslice)
# Convert flippedSlice to PIL for color extraction
flippedSlice_pil = Image.fromarray(flippedSlice)
# Define patch size for color sampling (e.g., 10x10 pixels)
patch_size = 100
patch_colors = []
# Loop through patches in the image
for i in range(0, flippedSlice_pil.width, patch_size):
for j in range(0, flippedSlice_pil.height, patch_size):
# Crop a patch from the original image
patch = flippedSlice_pil.crop((i, j, i + patch_size, j + patch_size))
patch_colors += patch.getcolors(patch_size * patch_size)
# Calculate the dominant color from all patches
max_count = 0
dominant_color = None
tolerance = 5
black_threshold = 30 # Max RGB value for a color to be considered "black"
white_threshold = 225 # Min RGB value for a color to be considered "white"
for count, color in patch_colors:
# Exclude colors within the black and white ranges
if not (all(c <= black_threshold for c in color) or all(c >= white_threshold for c in color)):
# Update if the current color has a higher count than previous max
if count > max_count:
max_count = count
dominant_color = color
# Append dominant color to ColorCheck and update NewColors
if dominant_color is not None:
ColorCheck.append(dominant_color)
NewColors = None
for color in ColorCheck:
# Check if the current color is within the tolerance
print("color = ",color)
print("dominant_color = ",dominant_color)
if (abs(color[0] - dominant_color[0]) < 20 and
abs(color[1] - dominant_color[1]) < 20 and
abs(color[2] - dominant_color[2]) < 20):
NewColors = (color[2], color[1], color[0]) # Set the new color
break
else:
# If no color in ColorCheck meets the tolerance, use the dominant color
NewColors = (dominant_color[2], dominant_color[1], dominant_color[0])
if NewColors not in ColorCheck:
ColorCheck.append(NewColors)
if flagcolor == 1:
SimilarAreaDictionary.at[index, 'Color'] = NewColors
# print(f"Updated Color at index {index} with {NewColors}.")
elif flagcolor == 2:
SimilarAreaDictionary.at[i, 'Color'] = NewColors
cv2.drawContours(imgg, [np.array(cntPoints)], -1, ([NewColors[2],NewColors[1],NewColors[0]]), thickness=-1)
annot11 = page2.add_polygon_annot( points=shapeeArea) # 'Polygon'
annot11.set_border(width=0.2)
annot11.set_colors(stroke=(int(NewColors[0])/255,int(NewColors[1])/255,int(NewColors[2])/255), fill= (int(NewColors[0])/255,int(NewColors[1])/255,int(NewColors[2])/255) )
annot11.set_info(content=str(polygon[1])+' sq m',subject='Area Measurement', title="ADR Team")
annot11.set_opacity(0.8)
# annot.set_line_ends(fitz.PDF_ANNOT_LE_DIAMOND, fitz.PDF_ANNOT_LE_CIRCLE)
annot11.update()
annot12 = page2.add_polyline_annot( points=shapeePerimeter ) # 'Polygon'
annot12.set_border(width=0.8)
annot12.set_colors(stroke=(int(NewColors[0])/255,int(NewColors[1])/255,int(NewColors[2])/255))
annot12.set_info(content=str(polygon[2])+' m',subject='Perimeter Measurement', title="ADR Team")
annot12.set_opacity(0.8)
# annot.set_line_ends(fitz.PDF_ANNOT_LE_DIAMOND, fitz.PDF_ANNOT_LE_CIRCLE)
annot12.update()
i += 1
alpha = 0.8 # Transparency factor.
page2.set_rotation(rotationOld)
Correct_img=flip(imgg)
image_new1 = cv2.addWeighted(Correct_img, alpha, img, 1 - alpha, 0)
SimilarAreaDictionary = SimilarAreaDictionary.fillna(' ')
# Define white color to filter out
white_color = (255, 255, 255)
# Delete rows where 'Guess' equals white_color
SimilarAreaDictionary = SimilarAreaDictionary[SimilarAreaDictionary['Color'] != white_color]
# Reset the index to update row numbering
SimilarAreaDictionary.reset_index(drop=True, inplace=True)
grouped_df = SimilarAreaDictionary.groupby('Color').agg({
'Guess':'first',
'Occurences': 'sum', # Sum of occurrences for each color
'Area':'first',
'Total Area': 'sum', # Sum of areas for each color
'Perimeter':'first',
'Total Perimeter': 'sum', # Sum of perimeters for each color
'Length':'first',
'Total Length':'first',
'Texts':'first',
'Comments':'first'
}).reset_index()
SimilarAreaDictionary = grouped_df
# doc.save(OutputPdfStage1)
modified_pdf_data = doc.tobytes()
OutputPdfStage2=adjustannotations(modified_pdf_data)
# with open("Adjusted_PDF.pdf", "wb") as f:
# f.write(OutputPdfStage2)
# doc2 = fitz.open(stream=OutputPdfStage2, filetype="pdf")
# doc2 = fitz.open(stream=OutputPdfStage2, filetype="pdf")
doc2 =fitz.open('pdf',OutputPdfStage2)
gc,spreadsheet_service,spreadsheetId, spreadsheet_url , namepathArr=google_sheet_Legend.legendGoogleSheets(SimilarAreaDictionary , pdfname,pdfpath)
# dbxTeam=tsadropboxretrieval.ADR_Access_DropboxTeam('user')
# md, res =dbxTeam.files_download(path= pdfpath+pdfname)
# data = res.content
# doc=fitz.open("pdf", data)
# list1=pd.DataFrame(columns=['content', 'creationDate', 'id', 'modDate', 'name', 'subject', 'title'])
list1=pd.DataFrame(columns=['content', 'id', 'subject','color'])
# for page in doc:
for page in doc2:
# Iterate through annotations on the page
for annot in page.annots():
# Get the color of the annotation
annot_color = annot.colors
if annot_color is not None:
# annot_color is a dictionary with 'stroke' and 'fill' keys
stroke_color = annot_color.get('stroke') # Border color
fill_color = annot_color.get('fill') # Fill color
if fill_color:
v='fill'
# print('fill')
if stroke_color:
v='stroke'
x,y,z=int(annot_color.get(v)[0]*255),int(annot_color.get(v)[1]*255),int(annot_color.get(v)[2]*255)
list1.loc[len(list1)] =[annot.info['content'],annot.info['id'],annot.info['subject'],[x,y,z]]
print('LISTTT',list1)
return doc2,image_new1, SimilarAreaDictionary ,spreadsheetId, spreadsheet_url , namepathArr , list1,hatched_areas
# doc.save('Testing(2.7).pdf')
# return doc,image_new1#, SimilarAreaDictionary ,spreadsheetId, spreadsheet_url , namepathArr , list1,hatched_areas
# datadoc='/content/3.3 - Ceiling finishes - Example 1 - Sheet 1.pdf' #pdf path here
# dxfpath='/content/3.3 - Ceiling finishes - Example 1 - Sheet 1.dxf'#dxfpath here
# dxfratio=28.3464527867108
# doc,image_new1=mainFunctionDrawImgPdf(datadoc,dxfpath, dxfratio)
# cv2_imshow(image_new1)