import tkinter import threading from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from PIL import Image import os import qrcode from tkinter import filedialog import time import datetime from dateutil.relativedelta import relativedelta from selenium.webdriver.common.action_chains import ActionChains import json import requests DropDownLabels = ["Street: ", "Number: ", "Postal code: ", "City: ", "Sonnendach URL: ", "Suitability", "Image Filename Map: ", "Image Filename Production: ", "Image Filename qrcode: ", "PV Production 50", "PV Production 75", "PV Production 100", "Value Electricity production", "Roof area"] file_split_char = "," OptionList = [] exit = False stopThread = False filename_adresslist = "" adresslist = "" step = 0 service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service) driver.minimize_window() outputtext = "Welcome to application Sonnendach\n" columnIndexes = [] def search_adresses(adress_list, filename_adresslist, driver, mainText): global file_split_char global stopThread global columnIndexes global outputtext global checkBoxCreateImages #Create Direcotrys to save screenshots and qrcodes if they don't exist yet. if(not os.path.exists("screenshots")): os.makedirs("screenshots", exist_ok=False) if (not os.path.exists("qrcodes")): os.makedirs("qrcodes", exist_ok=False) startTime = datetime.datetime.now() address_count = len(adress_list) #Search for each adress in the list and create screenshots and qrcodes for i in range(address_count): line = adress_list[i] adress = line.split(file_split_char) if((line != adress_list[0]) & (adress[columnIndexes[4]] == "") & (len(adress[columnIndexes[0]]) > 0) & (len(adress[columnIndexes[1]]) > 0) & (len(adress[columnIndexes[2]]) > 0) & (len(adress[columnIndexes[3]]) > 0)): search_string = adress[columnIndexes[0]] + " " + adress[columnIndexes[1]] + " " + adress[columnIndexes[2]] + " " + adress[columnIndexes[3]] if(createImages.get() > 0): search_bar = driver.find_element(By.ID, "searchTypeahead1") search_bar.send_keys(Keys.CONTROL + "a") search_bar.send_keys(Keys.DELETE) search_bar.send_keys(search_string) driver.implicitly_wait(10) time.sleep(0.7) found = False suggestions = driver.find_elements(By.XPATH, "//div[@class='tt-suggestion tt-selectable']") for suggestion in suggestions: if(suggestion.text == search_string): found = True suggestion.click() driver.implicitly_wait(10) break if(found): time.sleep(0.7) url = driver.current_url suitability = driver.find_element(By.ID, "eignung").text pv_Production50 = driver.find_element(By.ID, "pv50").text.replace("'", "") pv_Production75 = driver.find_element(By.ID, "pv75").text.replace("'", "") pv_Production100 = driver.find_element(By.ID, "pv100").text.replace("'", "") value_electricity_production = driver.find_elements(By.XPATH, "//h2[@id='TitelSolarstrom']//strong")[2].text.replace("'", "").replace(" Franken", "") roof_area = driver.find_element(By.ID, "areaOutput").text time.sleep(2) image_filename = suitability + " - " + search_string image_folder_map = "screenshots/" image_filename_map = image_filename + " map" + ".png" image_folder_production = "screenshots/" image_filename_production = image_filename + " production" + ".png" image_folder_qrcode = "qrcodes/" image_filename_qrcode = image_filename + " qrcode" + ".png" # Create QR-Code qr = qrcode.QRCode(version=1, box_size=10, border=5) qr.add_data(url) qr.make(fit=True) qr.make_image(fill='black', back_color='white').save(image_folder_qrcode + image_filename_qrcode) # Create Screenshot of Map actions = ActionChains(driver) try: driver.execute_script("""var l = document.getElementsByClassName("ol-zoom ol-unselectable ol-control")[0]; l.parentNode.removeChild(l);""") except: pass mapElement = driver.find_element(By.XPATH, "//div[@id='map']//div[@class='ol-viewport']") location = mapElement.location size = mapElement.size x = location["x"] y = location["y"] w = x + size["width"] h = size["height"] area = (x, 0, w, h) driver.execute_script("window.scrollTo(0, " + str(y) + ")") time.sleep(1) driver.save_screenshot(image_folder_map + image_filename_map) time.sleep(0.2) Image.open(image_folder_map + image_filename_map).crop(area).save( image_folder_map + image_filename_map) # Create Screenshot of Chart try: chartElement = driver.find_elements(By.XPATH, "//div[@id='chart']//*")[0] location = chartElement.location size = chartElement.size x = location["x"] y = location["y"] w = x + size["width"] h = size["height"] area = (x, 0, w, h) driver.execute_script("window.scrollTo(0, " + str(y) + ")") time.sleep(1) driver.save_screenshot(image_folder_production + image_filename_production) time.sleep(0.2) Image.open(image_folder_production + image_filename_production).crop(area).save( image_folder_production + image_filename_production) except: image_filename_production = "not-found" print("saved Address: " + search_string) else: url = "not-found" suitability = "not-found" image_filename_map = "not-found" image_filename_production = "not-found" image_filename_qrcode = "not-found" pv_Production50 = "not-found" pv_Production75 = "not-found" pv_Production100 = "not-found" value_electricity_production = "not-found" roof_area = "not-found" print("not found: " + search_string) else: # If no images must be created try: # Address to coordinates params = { "type": "locations", "searchText": search_string, "lang": "de"} locationsResponse = requests.get('https://api3.geo.admin.ch/rest/services/api/SearchServer', params=params) locations = json.loads(locationsResponse.content.decode()) firstLocation = locations['results'][0]['attrs'] # you need the coordinates from other services, once you have them coordinates = (firstLocation['y'], firstLocation['x']) # example coordinates coordinates_str = ','.join(map(str, coordinates)) tolerance = 1 # this is wrong, bbox should be computed using tolerance and pixel size, but there are no problems for the # purpose of this exercise bbox = (coordinates[0] - tolerance, coordinates[1] - tolerance, coordinates[0] + tolerance, coordinates[1] + tolerance) bbox_str = ','.join(map(str, bbox)) params = { "geometryType": "esriGeometryPoint", "returnGeometry": "true", "layers": "all:ch.bfe.solarenergie-eignung-daecher", "geometry": coordinates_str, "mapExtent": bbox_str, "imageDisplay": "1391,1070,96", "tolerance": tolerance, "order": "distance", "lang": "de", } # retrieve one roof and the building_id associated response_one_roof = requests.get('https://api3.geo.admin.ch/rest/services/api/MapServer/identify', params=params) response_one_roof_decoded = json.loads(response_one_roof.content.decode()) results = (response_one_roof_decoded['results']) # for result in results: # print(result) building_id = (response_one_roof_decoded)['results'][0]['attributes']['building_id'] # use the building_id of previous request to get all roofs associated with this building_id params = { 'layer': 'ch.bfe.solarenergie-eignung-daecher', 'searchField': 'building_id', 'contains': 'false', 'searchText': str(building_id), 'returnGeometry': 'false' } response_all_roofs = requests.get('https://api3.geo.admin.ch/rest/services/api/MapServer/find', params=params) response_all_roofs_decoded = json.loads(response_all_roofs.content.decode())['results'] best_roof = response_all_roofs_decoded[0] # TODO: Find best roof for roof in response_all_roofs_decoded: if(float(roof['attributes']['flaeche']) > float(best_roof['attributes']['flaeche'])): best_roof = roof url = "https://www.uvek-gis.admin.ch/BFE/sonnendach/index.html?featureId=" + str(best_roof['featureId']) + "&lang=de" suitability = str(best_roof['attributes']['klasse']) image_filename_map = "not-created" image_filename_production = "not-created" image_filename_qrcode = "not-created" pv_Production100 = str(float(best_roof['attributes']['gstrahlung'])*0.2*0.8) pv_Production75 = str(float(pv_Production100) * 0.75) pv_Production50 = str(float(pv_Production100) * 0.5) value_electricity_production = str(best_roof['attributes']['finanzertrag']) roof_area = str(best_roof['attributes']['flaeche']) print("saved Address: " + search_string) except: url = "not-found" suitability = "not-found" image_filename_map = "not-found" image_filename_production = "not-found" image_filename_qrcode = "not-found" pv_Production50 = "not-found" pv_Production75 = "not-found" pv_Production100 = "not-found" value_electricity_production = "not-found" roof_area = "not-found" print("Address " + str(i) + " of " + str(address_count)) timediff = relativedelta(datetime.datetime.now(), startTime) print("Process is running since %d years %d months %d days %d hours %d minutes %d seconds" % (timediff.years, timediff.months, timediff.days, timediff.hours, timediff.minutes, timediff.seconds)) #Write back into file adress[columnIndexes[4]] = url adress[columnIndexes[5]] = suitability adress[columnIndexes[6]] = image_filename_map adress[columnIndexes[7]] = image_filename_production adress[columnIndexes[8]] = image_filename_qrcode adress[columnIndexes[9]] = pv_Production50 adress[columnIndexes[10]] = pv_Production75 adress[columnIndexes[11]] = pv_Production100 adress[columnIndexes[12]] = value_electricity_production adress[columnIndexes[13]] = roof_area adress_file = open(filename_adresslist, "w", encoding="utf-8") new_line_string = "" for j in adress: new_line_string = new_line_string + j + file_split_char adress_list[i] = new_line_string new_adress_list = "" for j in adress_list: new_adress_list = new_adress_list + (j) + "\n" adress_file.write(new_adress_list) adress_file.close() if(stopThread == True): print("closing Thread") return outputtext = outputtext + "Reached end of list.\nPress Exit to close the application." + "\n" mainText.config(text=outputtext) print("closing Thread") return def read_adresslist(filename_adresslist): returnvalues = [] try: adress_file = open(filename_adresslist, "r", encoding="utf-8") adress_list = adress_file.read().splitlines() adress_file.close() returnvalues.append(True) returnvalues.append(adress_list) except: returnvalues.append(False) return returnvalues def createFrameFileColums(headers): global OptionList global DropDownLabels for header in headers: OptionList.append(header) desctext = tkinter.Label(frameFileColums, text="Please select Headers of Columns") desctext.grid(row=1, column=1, padx=10, pady=3) frameFileColums1 = tkinter.Frame(frameFileColums) frameFileColums1.grid(row=2, column=1, padx=10, pady=3) for i in range(len(DropDownLabels)): exec("text" + str(i+1) + " = tkinter.Label(frameFileColums1, text=\"" + DropDownLabels[i] + "\")") exec("text" + str(i+1) + ".grid(row=" + str(i+1) + ", column=1, padx=10, pady=3)") exec("dropdown" + str(i+1) + " = tkinter.OptionMenu(frameFileColums1, variablesDropdown[" + str(i) + "], *OptionList)") exec("dropdown" + str(i+1) + ".grid(row=" + str(i+1) + ", column=2, padx=10, pady=3)") def getColumnIndex(): global OptionList global columnIndexes for j in range(len(DropDownLabels)): for i in range(len(OptionList)): if(OptionList[i] == variablesDropdown[j].get()): columnIndexes.append(i) def command(): global exit global stopThread global adresslist global step global driver global filename_adresslist global outputtext global thread_search_adresses global file_split_char if(exit): print("Command Exit received") stopThread = True try: thread_search_adresses.join() except: pass print("Thread is closed") root.quit() print("Application is closed") driver.quit() print("Webdriver is closed") elif(step == 0): #Schritt 1 filename_adresslist = filedialog.askopenfilename() outputtext = outputtext + "Searching File " + filename_adresslist + "\n" mainText.config(text=outputtext) adress_list_result = read_adresslist(filename_adresslist) if(adress_list_result[0]): adresslist = adress_list_result[1] outputtext = outputtext + "File " + filename_adresslist + " found." + "\n" mainText.config(text=outputtext) button1.config(text="read file") createFrameFileColums(adresslist[0].split(file_split_char)) frameFileColums.grid(row=2, column=1) step += 1 else: outputtext = outputtext + "File " + filename_adresslist + " not found." + "\n" mainText.config(text=outputtext) exit = True button1.grid_remove() elif (step == 1): # TODO: check if dropdowns are selected getColumnIndex() outputtext = outputtext + "Reading File " + filename_adresslist + " done." + "\n" mainText.config(text=outputtext) frameFileColums.grid_remove() button1.config(text="open Webbrowser") step += 1 elif(step == 2): # Schritt 2 outputtext = outputtext + "Website is opening" + "\n" mainText.config(text=outputtext) try: driver.maximize_window() driver.get("https://www.uvek-gis.admin.ch/BFE/sonnendach/") driver.implicitly_wait(20) outputtext = outputtext + "opening website was done.\nPrepare the browser window to create screenshots." + "\n" mainText.config(text=outputtext) button1.config(text="start process") step += 1 except: outputtext = outputtext + "opening website failed" + "\n" mainText.config(text=outputtext) exit = True button1.grid_remove() elif(step == 3): thread_search_adresses = threading.Thread(target=search_adresses, args=(adresslist, filename_adresslist, driver, mainText)) thread_search_adresses.start() print("Thread started") outputtext = outputtext + "process has started." + "\n" mainText.config(text=outputtext) button1.grid_remove() exit = True def command_exit(): global exit exit = True command() root = tkinter.Tk() root.protocol("WM_DELETE_WINDOW", command_exit) root.wm_title("Sonnendach") mainText = tkinter.Label(root, text=outputtext, width=80) mainText.grid(row=1, column=1, padx=10, pady=3) frameFileColums = tkinter.Frame(root) variablesDropdown = [] for i in range(len(DropDownLabels)): variablesDropdown.append(tkinter.StringVar(root)) frameButtons = tkinter.Frame(root) frameButtons.grid(row=3, column=1) createImages = tkinter.IntVar() checkBoxCreateImages = tkinter.Checkbutton(frameButtons, text="create images", variable=createImages) checkBoxCreateImages.grid() button1 = tkinter.Button(frameButtons, text="Select Adresslist file", command=command, width=20, height=2, bg="#FCCA03") button1.grid(row=3, column=1, padx=10, pady=3) button2 = tkinter.Button(frameButtons, text="EXIT", command=command_exit, width=20, height=2, bg="#FCCA03") button2.grid(row=3, column=2, padx=10, pady=3) root.mainloop()