Spaces:
Sleeping
Sleeping
import pandas as pd | |
import numpy as np | |
import math | |
from statistics import mean, stdev | |
from collections import defaultdict | |
import shapely | |
import shapely.wkt | |
from shapely.geometry import Point, MultiPoint, LineString, MultiLineString, Polygon, LinearRing | |
from shapely.ops import voronoi_diagram, substring, unary_union, nearest_points | |
from shapely import affinity | |
from shapely.prepared import prep | |
import cv2 as cv | |
def segments(polyline): | |
return list(map(LineString, zip(polyline.coords[:-1], polyline.coords[1:]))) | |
def scale_move_x(x, xmin_abs, scale): | |
xn = (x / scale) - 1 - xmin_abs | |
return xn | |
def scale_move_y(y, ymin_abs, scale): | |
yn = (y / scale) - 1 - ymin_abs | |
return yn | |
def scale_area(a, scale): | |
a = a / (scale**2) | |
return a | |
def scale_perimeter(p, scale): | |
p = p / scale | |
return p | |
def wall_segment_cosine(direction, apa_line_seg): | |
seg_s = list(apa_line_seg.coords)[0] | |
seg_e = list(apa_line_seg.coords)[1] | |
normal_x = seg_e[0] - seg_s[0] | |
normal_y = seg_e[1] - seg_s[1] | |
normal_s = (-normal_y, normal_x) | |
normal_e = (normal_y, -normal_x) | |
o = np.array([-normal_y, normal_x]) | |
w = np.array([normal_y, -normal_x]) | |
if direction == "south": | |
d = np.array([-normal_y, normal_x-1]) | |
if direction == "east": | |
d = np.array([-normal_y+1, normal_x]) | |
if direction == "north": | |
d = np.array([-normal_y, normal_x+1]) | |
if direction == "west": | |
d = np.array([-normal_y-1, normal_x]) | |
od = d - o | |
ow = w - o | |
cosine = np.dot(od, ow) / (np.linalg.norm(od) * np.linalg.norm(ow)) | |
return cosine | |
# Dir_S_longestedge, Dir_N_longestedge, Dir_W_longestedge, Dir_E_longestedge, Dir_S_max, Dir_N_max, Dir_W_max, Dir_E_max, Facade_length, Facade_ratio | |
def wall_direction_ratio(apa_line, apa_wall): | |
apa_wall_O = [i for indx,i in enumerate(segments(apa_line)) if apa_wall[indx] == "O"] | |
apa_wall_O = MultiLineString(apa_wall_O) | |
wall_O_length = [] | |
wall_O_south = [] | |
wall_O_east = [] | |
wall_O_north = [] | |
wall_O_west = [] | |
apa_wall_O_num = len(apa_wall_O.geoms) | |
if apa_wall_O_num > 0: | |
for i in range(apa_wall_O_num): | |
wall_seg = apa_wall_O.geoms[i] | |
wall_length = wall_seg.length | |
south_cos = wall_segment_cosine("south", wall_seg) | |
east_cos = wall_segment_cosine("east", wall_seg) | |
north_cos = wall_segment_cosine("north", wall_seg) | |
west_cos = wall_segment_cosine("west", wall_seg) | |
if south_cos < 0: | |
south_cos = 0 | |
if east_cos < 0: | |
east_cos = 0 | |
if north_cos < 0: | |
north_cos = 0 | |
if west_cos < 0: | |
west_cos = 0 | |
wall_O_length.append(wall_length) | |
wall_O_south.append(south_cos) | |
wall_O_east.append(east_cos) | |
wall_O_north.append(north_cos) | |
wall_O_west.append(west_cos) | |
max_length_index = np.array(wall_O_length).argmax() | |
Dir_S_longestedge = wall_O_south[max_length_index] | |
Dir_N_longestedge = wall_O_north[max_length_index] | |
Dir_W_longestedge = wall_O_west[max_length_index] | |
Dir_E_longestedge = wall_O_east[max_length_index] | |
Dir_S_max = max(wall_O_south) | |
Dir_N_max = max(wall_O_north) | |
Dir_W_max = max(wall_O_west) | |
Dir_E_max = max(wall_O_east) | |
Facade_length = apa_wall_O.length | |
apa_line_length = apa_line.length | |
Facade_ratio = Facade_length / apa_line_length | |
else: | |
Dir_S_longestedge = 0 | |
Dir_N_longestedge = 0 | |
Dir_W_longestedge = 0 | |
Dir_E_longestedge = 0 | |
Dir_S_max = 0 | |
Dir_N_max = 0 | |
Dir_W_max = 0 | |
Dir_E_max = 0 | |
Facade_length = 0 | |
Facade_ratio = 0 | |
return Dir_S_longestedge, Dir_N_longestedge, Dir_W_longestedge, Dir_E_longestedge, Dir_S_max, Dir_N_max, Dir_W_max, Dir_E_max, Facade_length, Facade_ratio | |
# apa_geo | |
def apartment_perimeter(apa_geo): | |
perimeter =apa_geo.length | |
return perimeter | |
def apartment_area(apa_geo): | |
area =apa_geo.area | |
return area | |
def boundingbox(apa_geo): | |
boundingbox = apa_geo.bounds | |
return boundingbox | |
# BBox_width_x, BBox_height_y, Aspect_ratio, Extent, ULC_x, ULC_y, LRC_x, LRC_y | |
def boundingbox_features(apa_geo): | |
# [Aspect_ratio, Extent] ---> https://docs.opencv.org/3.4/d1/d32/tutorial_py_contour_properties.html | |
bbox_xy = boundingbox(apa_geo) | |
bbox_geo = Polygon([(bbox_xy[0], bbox_xy[1]), (bbox_xy[2], bbox_xy[1]), (bbox_xy[2], bbox_xy[3]), (bbox_xy[0], bbox_xy[3])]) | |
BBox_width_x = bbox_xy[2] - bbox_xy[0] | |
BBox_height_y = bbox_xy[3] - bbox_xy[1] | |
Aspect_ratio = BBox_width_x / BBox_height_y | |
bbox_geo_area = bbox_geo.area | |
Area = apartment_area(apa_geo) | |
Extent = Area / bbox_geo_area | |
ULC_x = bbox_xy[0] | |
ULC_y = bbox_xy[3] | |
LRC_x = bbox_xy[2] | |
LRC_y = bbox_xy[1] | |
return BBox_width_x, BBox_height_y, Aspect_ratio, Extent, ULC_x, ULC_y, LRC_x, LRC_y | |
# Max_diameter | |
def max_diameter(apa_geo): | |
# [Max_diameter] ---> https://www.mvtec.com/doc/halcon/12/en/diameter_region.html | |
apa_coor = list(apa_geo.exterior.coords) | |
pp_dis_lst = [] | |
for i in apa_coor: | |
for j in apa_coor: | |
pp_dis = Point(i).distance(Point(j)) | |
pp_dis_lst.append(pp_dis) | |
max_diameter = max(pp_dis_lst) | |
return max_diameter | |
def fractality(apa_geo): | |
# [Fractality] ---> https://onlinelibrary.wiley.com/doi/epdf/10.1111/j.1538-4632.2000.tb00419.x | |
# Basaraner, M. and Cetinkaya, S. (2017) ‘Performance of shape indices and classification schemes for characterising perceptual shape complexity of building footprints in GIS’, International Journal of Geographical Information Science, 31(10), pp. 1952–1977. doi:10.1080/13658816.2017.1346257. | |
Area = apartment_area(apa_geo) | |
Perimeter = apartment_perimeter(apa_geo) | |
fractality = 1 - ((math.log(Area) / (2 * math.log(Perimeter)))) | |
return fractality | |
def circularity(apa_geo): | |
# [Circularity] ---> https://www.mvtec.com/doc/halcon/12/en/circularity.html | |
apa_coor = list(apa_geo.exterior.coords) | |
op_dis_lst = [] | |
for i in apa_coor: | |
op_dis = Point((0, 0)).distance(Point(i)) | |
op_dis_lst.append(op_dis) | |
Max_radius = max(op_dis_lst) | |
Area = apartment_area(apa_geo) | |
circularity = Area / ((math.pi) * (Max_radius**2)) | |
return circularity | |
def outer_radius(p_4_cv, xmin_abs, ymin_abs, scale): | |
# [Outer_radius] ---> https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga8ce13c24081bbc7151e9326f412190f1 | |
(xmin,ymin),radius = cv.minEnclosingCircle(p_4_cv) | |
mini_Enclosing_Cir_x = scale_move_x(xmin, xmin_abs, scale) | |
mini_Enclosing_Cir_y = scale_move_y(ymin, ymin_abs, scale) | |
mini_Enclosing_Cir_radius = scale_perimeter(radius, scale) | |
outer_radius = mini_Enclosing_Cir_radius | |
return outer_radius | |
def inner_radius(apa_geo, apa_line): | |
# [Inner_radius] ---> https://www.sthu.org/blog/14-skeleton-offset-topology/index.html | |
dis_p = [] | |
for i in np.arange(0, apa_line.length, 0.1): | |
s = substring(apa_line, i, i+0.1) | |
dis_p.append(s.boundary.geoms[0]) | |
mp = MultiPoint(dis_p) | |
regions = voronoi_diagram(mp) | |
vo_p = [] | |
for i in range(len(regions.geoms)): | |
vo = regions.geoms[i] | |
b = list(vo.exterior.coords) | |
for j in range(len(b)): | |
p = Point(b[j]) | |
vo_p.append(p) | |
vo_p = MultiPoint(vo_p) | |
vo_p = unary_union(vo_p) | |
vo_p_b = [] | |
for i in range(len(vo_p.geoms)): | |
t_c_p = vo_p.geoms[i] | |
pc = apa_geo.contains(t_c_p) | |
vo_p_b.append(pc) | |
vo_filtered_p = [i for indx,i in enumerate(vo_p.geoms) if vo_p_b[indx] == True] | |
vo_d = [] | |
for i in range(len(vo_filtered_p)): | |
c = Point(vo_filtered_p[i]) | |
d_min = c.distance(apa_line) | |
vo_d.append(d_min) | |
vo_r_max = max(vo_d) | |
vo_r_max_index = vo_d.index(vo_r_max) | |
vo_c_max = vo_filtered_p[vo_r_max_index] | |
vo_c_max = list(vo_c_max.coords) | |
max_Inner_Circle_x = vo_c_max[0][0] | |
max_Inner_Circle_y = vo_c_max[0][1] | |
max_Inner_Circle_r = vo_r_max | |
inner_radius = max_Inner_Circle_r | |
return inner_radius | |
def roundness_features(apa_line): | |
# [Dist_mean, Dist_sigma, Roundness] ---> https://www.mvtec.com/doc/halcon/12/en/roundness.html | |
rou_p = [] | |
for i in np.arange(0, apa_line.length, 0.5): | |
s = substring(apa_line, i, i+0.5) | |
rou_p.append(s.boundary.geoms[0]) | |
rp = MultiPoint(rou_p) | |
ro_dis = [] | |
for i in range(len(rp.geoms)): | |
rpp = rp.geoms[i] | |
ro = Point(rpp).distance(Point((0, 0))) | |
ro_dis.append(ro) | |
dist_mean = mean(ro_dis) | |
# dist_sigma = stdev(ro_dis) | |
dev_lst = [] | |
for i in ro_dis: | |
dev = (i - dist_mean)**2 | |
dev_lst.append(dev) | |
dist_sigma = mean(dev_lst) | |
dist_sigma = math.sqrt(dist_sigma) | |
roundness = 1 - (dist_sigma/dist_mean) | |
return dist_mean, dist_sigma, roundness | |
def compactness(apa_geo): | |
# [Compactness] ---> https://fisherzachary.github.io/public/r-output.html | |
Area = apartment_area(apa_geo) | |
Perimeter = apartment_perimeter(apa_geo) | |
compactness = (4*(math.pi)) * (Area / (Perimeter**2)) | |
return compactness | |
def equivalent_diameter(apa_geo): | |
# https://docs.opencv.org/4.x/d1/d32/tutorial_py_contour_properties.html | |
Area = apartment_area(apa_geo) | |
equivalent_diameter = math.sqrt((4 * Area) / math.pi) | |
return equivalent_diameter | |
def shape_membership_index(apa_line): | |
# [Shape_membership_index] ---> Basaraner, M. and Cetinkaya, S. (2017) ‘Performance of shape indices and classification schemes for characterising perceptual shape complexity of building footprints in GIS’, International Journal of Geographical Information Science, 31(10), pp. 1952–1977. doi:10.1080/13658816.2017.1346257. | |
line_smi = LineString([(0, 0), (30, 0)]) | |
numl = 30 | |
line_rot_degree = 360 / numl | |
line_rot = [] | |
for an in range(numl): | |
ang = an*line_rot_degree | |
lr = affinity.rotate(line_smi, ang, (0, 0)) | |
line_rot.append(lr) | |
line_rot = MultiLineString(line_rot) | |
smip = shapely.intersection(apa_line, line_rot) | |
simo_dis = [] | |
for i in range(len(smip.geoms)): | |
sim_p = smip.geoms[i] | |
simo = Point(sim_p).distance(Point((0, 0))) | |
simo_dis.append(simo) | |
sim_r_max = max(simo_dis) | |
simo_maxd = [] | |
for j in simo_dis: | |
rmax_d = j / sim_r_max | |
simo_maxd.append(rmax_d) | |
simo_maxd_mean = mean(simo_maxd) | |
simo_rad = [] | |
for j in range(len(simo_dis)): | |
s = simo_dis[j] | |
if j == (len(simo_dis) - 1): | |
nu = 0 | |
else: | |
nu = j+1 | |
e = simo_dis[nu] | |
if s <= e: | |
a = np.array([1,s]) | |
b = np.array([0,s]) | |
c = np.array([1,e]) | |
else: | |
a = np.array([1,e]) | |
b = np.array([0,e]) | |
c = np.array([1,s]) | |
ba = a - b | |
bc = c - b | |
cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc)) | |
angle_rad = np.arccos(cosine_angle) | |
simo_rad.append(angle_rad) | |
simo_rad_min = min(simo_rad) | |
simo_rad_max = max(simo_rad) | |
simo_cos = math.cos(simo_rad_max - simo_rad_min) | |
shape_membership_index = simo_cos * simo_maxd_mean | |
return shape_membership_index | |
def convexity(p_4_cv, apa_geo, xmin_abs, ymin_abs, scale): | |
# [Convexity] ---> Basaraner, M. and Cetinkaya, S. (2017) ‘Performance of shape indices and classification schemes for characterising perceptual shape complexity of building footprints in GIS’, International Journal of Geographical Information Science, 31(10), pp. 1952–1977. doi:10.1080/13658816.2017.1346257. | |
hull = cv.convexHull(p_4_cv) | |
hull_x = [] | |
hull_y = [] | |
for h in range(len(hull)): | |
h_x = hull[h][0][0] | |
h_x = scale_move_x(h_x, xmin_abs, scale) | |
hull_x.append(h_x) | |
h_y = hull[h][0][1] | |
h_y = scale_move_y(h_y, ymin_abs, scale) | |
hull_y.append(h_y) | |
hull_xy = [] | |
for i in range(len(hull_x)): | |
hx = hull_x[i] | |
hy = hull_y[i] | |
hull_xy.append((hx, hy)) | |
hull_geo = Polygon(hull_xy) | |
Hull_area = hull_geo.area | |
Area = apartment_area(apa_geo) | |
convexity = Area / Hull_area | |
return convexity, hull_geo | |
def rectangle_features(p_4_cv, apa_geo, xmin_abs, ymin_abs, scale): | |
# [Rectangularity] ---> Basaraner, M. and Cetinkaya, S. (2017) ‘Performance of shape indices and classification schemes for characterising perceptual shape complexity of building footprints in GIS’, International Journal of Geographical Information Science, 31(10), pp. 1952–1977. doi:10.1080/13658816.2017.1346257. | |
rect = cv.minAreaRect(p_4_cv) | |
miniRect_rotation_angle = rect[2] | |
box = cv.boxPoints(rect) | |
box = np.intp(box) | |
miniRect_x = [] | |
miniRect_y = [] | |
for b in range(len(box)): | |
b_x = box[b][0] | |
b_x = scale_move_x(b_x, xmin_abs, scale) | |
miniRect_x.append(b_x) | |
b_y = box[b][1] | |
b_y = scale_move_y(b_y, ymin_abs, scale) | |
miniRect_y.append(b_y) | |
miniRec_xy = [] | |
for i in range(len(miniRect_x)): | |
minirecx = miniRect_x[i] | |
minirecy = miniRect_y[i] | |
miniRec_xy.append((minirecx, minirecy)) | |
miniRect_geo = Polygon(miniRec_xy) | |
miniRect_area = miniRect_geo.area | |
Area = apartment_area(apa_geo) | |
rectangularity = Area / miniRect_area | |
rect_phi = (miniRect_rotation_angle * math.pi) / 180 | |
miniRect_line = miniRect_geo.boundary | |
miniRect_segments = segments(miniRect_line) | |
seg_len = [] | |
for s in miniRect_segments: | |
seg_len.append(s.length) | |
rect_width = max(seg_len) | |
rect_height = min(seg_len) | |
return rectangularity, rect_phi, rect_width, rect_height | |
def squareness(apa_geo): | |
# [Squareness] ---> Basaraner, M. and Cetinkaya, S. (2017) ‘Performance of shape indices and classification schemes for characterising perceptual shape complexity of building footprints in GIS’, International Journal of Geographical Information Science, 31(10), pp. 1952–1977. doi:10.1080/13658816.2017.1346257. | |
Area = apartment_area(apa_geo) | |
Perimeter = apartment_perimeter(apa_geo) | |
squareness = (4*(math.sqrt(Area))) / Perimeter | |
return squareness | |
def moments(apa_geo): | |
# https://leancrew.com/all-this/2018/01/python-module-for-section-properties/ | |
pts = list(apa_geo.exterior.coords) | |
if pts[0] != pts[-1]: | |
pts = pts + pts[:1] | |
x = [ c[0] for c in pts ] | |
y = [ c[1] for c in pts ] | |
sxx = syy = sxy = 0 | |
a = apartment_area(apa_geo) | |
cx = apa_geo.centroid.x | |
cy = apa_geo.centroid.y | |
for i in range(len(pts) - 1): | |
sxx += (y[i]**2 + y[i]*y[i+1] + y[i+1]**2)*(x[i]*y[i+1] - x[i+1]*y[i]) | |
syy += (x[i]**2 + x[i]*x[i+1] + x[i+1]**2)*(x[i]*y[i+1] - x[i+1]*y[i]) | |
sxy += (x[i]*y[i+1] + 2*x[i]*y[i] + 2*x[i+1]*y[i+1] + x[i+1]*y[i])*(x[i]*y[i+1] - x[i+1]*y[i]) | |
return sxx/12 - a*cy**2, syy/12 - a*cx**2, sxy/24 - a*cx*cy | |
def moment_index(apa_geo, Convexity, Compactness): | |
# https://www.researchgate.net/publication/228557311_A_COMBINED_AUTOMATED_GENERALIZATION_MODEL_BASED_ON_THE_RELATIVE_FORCES_BETWEEN_SPATIAL_OBJECTS | |
Ixx, Iyy, Ixy = moments(apa_geo) | |
ratio = max(Ixx, Iyy) / min(Ixx, Iyy) | |
# Convexity, Hull_geo = convexity(p_4_cv) | |
# Compactness = compactness(apa_geo) | |
moment_index = (Convexity * Compactness) / ratio | |
return moment_index | |
def ndetour_index(apa_geo, Hull_geo): | |
# [nDetour_index] ---> Basaraner, M. and Cetinkaya, S. (2017) ‘Performance of shape indices and classification schemes for characterising perceptual shape complexity of building footprints in GIS’, International Journal of Geographical Information Science, 31(10), pp. 1952–1977. doi:10.1080/13658816.2017.1346257. | |
Hull_line = Hull_geo.boundary | |
Hull_length = Hull_line.length | |
Area = apartment_area(apa_geo) | |
ndetour_index = (2 * math.sqrt(Area * math.pi)) / Hull_length | |
return ndetour_index | |
def ncohesion_index(apa_geo, grid_points): | |
# [nCohesion_index] ---> Basaraner, M. and Cetinkaya, S. (2017) ‘Performance of shape indices and classification schemes for characterising perceptual shape complexity of building footprints in GIS’, International Journal of Geographical Information Science, 31(10), pp. 1952–1977. doi:10.1080/13658816.2017.1346257. | |
grid_p = grid_points.geoms | |
grid_n = len(grid_p) | |
gg_dis_lst = [] | |
for i in grid_p: | |
for j in grid_p: | |
gg_dis = Point(i).distance(Point(j)) | |
gg_dis_lst.append(gg_dis) | |
Area = apartment_area(apa_geo) | |
ncohesion_index = (0.9054 * math.sqrt(Area / math.pi)) / (sum(gg_dis_lst) / (grid_n * (grid_n-1))) | |
return ncohesion_index | |
def nproximity_nspin_index(apa_geo, grid_points): | |
grid_p = grid_points.geoms | |
go_dis_lst = [] | |
for i in grid_p: | |
go_dis = Point(i).distance(Point(0,0)) | |
go_dis_lst.append(go_dis) | |
go_dis_mean = mean(go_dis_lst) | |
Area = apartment_area(apa_geo) | |
nproximity_index = ((2 / 3) * math.sqrt(Area / math.pi)) / go_dis_mean | |
nspin_index = (0.5 * (Area / math.pi)) / (go_dis_mean**2) | |
return nproximity_index, nspin_index | |
def nexchange_index(apa_geo): | |
Area = apartment_area(apa_geo) | |
eac_r = math.sqrt(Area / math.pi) | |
eac = Point(0,0).buffer(eac_r) | |
eac_inter = apa_geo.intersection(eac) | |
if eac_inter.geom_type == "Polygon": | |
eac_area = eac_inter.area | |
else: | |
eacga_lst = [] | |
for i in range(len(eac_inter.geoms)): | |
eacg = eac_inter.geoms[i] | |
eacga = eacg.area | |
eacga_lst.append(eacga) | |
eac_area = sum(eacga_lst) | |
nexchange_index = eac_area / Area | |
return nexchange_index | |
def nperimeter_index(apa_geo): | |
Area = apartment_area(apa_geo) | |
Perimeter = apartment_perimeter(apa_geo) | |
nperimeter_index = (2 * math.sqrt(math.pi * Area)) / Perimeter | |
return nperimeter_index | |
def ndepth_index(apa_geo, apa_line, grid_points): | |
moved_apa_line = apa_line | |
grid_p = grid_points.geoms | |
nea_len_lst = [] | |
for i in grid_p: | |
nea_line = LineString(nearest_points(moved_apa_line, i)) | |
nea_len = nea_line.length | |
nea_len_lst.append(nea_len) | |
nea_len_mean = mean(nea_len_lst) | |
Area = apartment_area(apa_geo) | |
ndepth_index = (3 * nea_len_mean) / math.sqrt(Area / math.pi) | |
return ndepth_index | |
def ngirth_index(apa_geo, Inner_radius): | |
Area = apartment_area(apa_geo) | |
ngirth_index = Inner_radius / math.sqrt(Area / math.pi) | |
return ngirth_index | |
def nrange_index(apa_geo, Outer_radius): | |
Area = apartment_area(apa_geo) | |
nrange_index = math.sqrt(Area / math.pi) / Outer_radius | |
return nrange_index | |
def ntraversal_index(apa_geo, apa_line): | |
rou_p = [] | |
for i in np.arange(0, apa_line.length, 0.5): | |
s = substring(apa_line, i, i+0.5) | |
rou_p.append(s.boundary.geoms[0]) | |
rp = MultiPoint(rou_p) | |
rp_n = len(rp.geoms) | |
bb_dis_lst = [] | |
for i in rp.geoms: | |
for j in rp.geoms: | |
bb_dis = Point(i).distance(Point(j)) | |
bb_dis_lst.append(bb_dis) | |
bb_dis_mean = sum(bb_dis_lst) / (rp_n * (rp_n-1)) | |
Area = apartment_area(apa_geo) | |
ntraversal_index = (4 * (math.sqrt(Area / math.pi) / math.pi)) / bb_dis_mean | |
return ntraversal_index | |