#!/usr/bin/env python # coding: utf-8 # In[54]: ### Description #This code written for 4th Question which is in the "P.I. Works - Geolocation Product Design Engineer Questionnaire". #It written on Jupyter Notebook using the Python 3 programming language. #It must be in the same directory as "buildings.kml" and "districts.kml" files to be used or file path or url must be specified. # Final Output: #Sorting of 3 districts having highest number of buildings with their number of buildings values # Ahmet Fırat KARAOĞLU # 11.10.2021 # In[55]: #Importing to necessary libraries import geopandas as gpd import fiona import operator # In[56]: #Reading the KML files with support of imported libraries gpd.io.file.fiona.drvsupport.supported_drivers['KML'] = 'rw' buildings = gpd.read_file('buildings.kml', driver='KML') districts = gpd.read_file('districts.kml', driver='KML') # In[57]: buildings #just take a look to buildings data # In[58]: buildings.type #just looked out of curiosity # In[59]: districts #just take a look to districts data # In[60]: #A function that finds intersections between district polygons and building polygons def intersect(build,distr): global list1 list1 = [] for j in range(len(distr)): #loop for all district values count = 0 for i in range(len(build)): #loop for all building values if build.geometry[i].intersects(distr.geometry[j]) == True: count = count +1 list1.append([distr.Name[j],count]) #append lists into the list. The appended lists includes the districts name and the number of intersecting buildings. global sorted_list1 sorted_list1 = sorted(list1, key=operator.itemgetter(1), reverse=True) #a new list that sorts the lists in the resulting list according to the number of buildings, from largest to smallest return sorted_list1 # In[61]: intersect(buildings,districts) #Let's call the function with our data and see the result # In[62]: #AYVANSARAY, BALAT and KOCAMUSTAFAPAŞA districts seem to have the highest number of buildings. #But there may be buildings located on different district boundaries at the same time. #In other words, a building can be located within the boundaries of both(or multi) districts due to its location. # In[63]: #To see if there is a difference between sum of the number of buildings in the districts just calculated and the number of buildings in our data. #If there is redundancy, the above comment is correct. sum1 = 0 for i in range(len(sorted_list1)): sum1 = sum1 + sorted_list1[i][1] sum1 - len(buildings) # In[64]: #As you can see, there are 363 extra values because there are buildings that intersect with more than one district. #So let's do an overlap calculation. # In[65]: #A function that finds overlaps between district polygons and building polygons def overlap(build,distr): global list2 list2 = [] for j in range(len(distr)): #loop for all district values count = 0 for i in range(len(build)): #loop for all building values if build.geometry[i].overlaps(distr.geometry[j]) == True: count = count +1 list2.append([distr.Name[j],count]) #append lists into the list. The appended lists includes the districts name and the number of intersecting buildings. global sorted_list2 sorted_list2 = sorted(list2, key=operator.itemgetter(1), reverse=True) #a new list that sorts the lists in the resulting list according to the number of buildings, from largest to smallest return sorted_list2 # In[66]: overlap(buildings,districts) #Let's call the function with our data and see the overlap results # In[67]: #As you can see there are some overlaps. #Let's look at the difference in num. of buildings between "intersection" and actual data versus the num. of "overlap" buildings. sum2 = 0 for i in range(len(sorted_list2)): sum2 = sum2 + sorted_list2[i][1] (sum1 - len(buildings)) - sum2 # In[68]: sum2 #(363 + 386) number of overlaps # In[69]: #The "overlap" function returns a value where two shapes intersect, but if the intersection results in an object different from both shapes. #As you can see, when overlapping buildings are added to the difference, the number of buildings found with the "intersection" function is 386 less. #This means that the result of the "overlap" function includes buildings that are also in more than one district because of their location(363), #and additionally includes buildings that overlap only with a district boundary because of their location(386). #As an example of the reason for this extra number of buildings(386), #a building that located on the border of a district polygon that is not adjacent to another district polygon can be given. # In[70]: #Let's ignore these all overlapping buildings(749) by using the result of "intersect" function and see if the ranking results change. #First, let's subtract the "overlap" function values according to the corresponding districts from the number of buildings calculated with the "intersect" function. list3 = [] for i in range(len(districts)): list3.append([ list1[i][0], list1[i][1] - list2[i][1] ]) #and create a new list with the subtraction result values sorted_list3 = sorted(list3, key=operator.itemgetter(1), reverse=True) #new list that sorts the building number values from the largest to the smallest sorted_list3 # In[71]: #The ranking of the three districts with the highest number of buildings remained unchanged with this calculations. #Let's print the final result. print("Sorting of 3 districts having highest number of buildings are:") for p in range(3): print(sorted_list3[p][0] + ":", sorted_list3[p][1], "(num. of buildings)") # In[72]: #The resulting building number values are approximate to reality.(few in reality) #Because all buildings considered as overlap were ignored from the relevant districts. #Since we do not have enough information, we can not know which district has overlapping buildings belong to with these data. #Thanks and healthy days # In[ ]: