diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d1cceb --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vscode +venv +solargeo.json +bfg-1.13.0.jar +*\google_creds.json +*\keys.json diff --git a/Aptfile b/Aptfile new file mode 100644 index 0000000..6f0ea40 --- /dev/null +++ b/Aptfile @@ -0,0 +1,4 @@ +libsm6 +libxrender1 +libfontconfig1 +libice6 \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..6f18d33 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn api:app \ No newline at end of file diff --git a/README.md b/README.md index 91a2b2c..247f147 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# EarthXHackothon - Hackathon Repo +# envohacks diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/__pycache__/get_map.cpython-38.pyc b/__pycache__/get_map.cpython-38.pyc new file mode 100644 index 0000000..c69e9d9 Binary files /dev/null and b/__pycache__/get_map.cpython-38.pyc differ diff --git a/__pycache__/get_prediction.cpython-38.pyc b/__pycache__/get_prediction.cpython-38.pyc new file mode 100644 index 0000000..5e42dc8 Binary files /dev/null and b/__pycache__/get_prediction.cpython-38.pyc differ diff --git a/__pycache__/get_size.cpython-38.pyc b/__pycache__/get_size.cpython-38.pyc new file mode 100644 index 0000000..587e20a Binary files /dev/null and b/__pycache__/get_size.cpython-38.pyc differ diff --git a/api.py b/api.py new file mode 100644 index 0000000..afe4450 --- /dev/null +++ b/api.py @@ -0,0 +1,51 @@ +from flask import Flask, request, jsonify +from detector import rooftop_detection as rd +from flask_cors import CORS + +import os +app = Flask(__name__) +CORS(app) + +@app.route('/') +def home(): + return '

Deployed to Wherever!

' + #Environment variables: os.environ['varName'] + +@app.route('/process', methods=['GET']) +def stuff(): + lat = float(request.args.get('lat')) + lon = float(request.args.get('lon')) + sol = float(request.args.get('solar')) + response = rd.get_roof_data(lat,lon) + image = response['image'] + #response.pop('image', None) + coeffecient = { + "prism": 0.75, + "flat": 1, + "slantedprism": 0.6, + "pyramid": 0.5, + "complex": 0.4 + } + response["adjusted"]=coeffecient[response["name"]] * sol + return jsonify(response) + + +if __name__ == "__main__": + app.run() + +# Sample request +# { +# "methods": "GET", +# "solar": "3.121", +# "lat": "7.721321321313", +# "lon": "7.721321321313", +# } + + +# Sample response +# { +# "category": "slantedprism", +# "image": image, +# "calculatedArea":"something something m^2", +# "powerRating":"something something kWh/ m^2", +# } \ No newline at end of file diff --git a/detector/__pycache__/get_map.cpython-38.pyc b/detector/__pycache__/get_map.cpython-38.pyc new file mode 100644 index 0000000..651a96a Binary files /dev/null and b/detector/__pycache__/get_map.cpython-38.pyc differ diff --git a/detector/__pycache__/get_prediction.cpython-38.pyc b/detector/__pycache__/get_prediction.cpython-38.pyc new file mode 100644 index 0000000..3fa9d3e Binary files /dev/null and b/detector/__pycache__/get_prediction.cpython-38.pyc differ diff --git a/detector/__pycache__/get_size.cpython-38.pyc b/detector/__pycache__/get_size.cpython-38.pyc new file mode 100644 index 0000000..e2af877 Binary files /dev/null and b/detector/__pycache__/get_size.cpython-38.pyc differ diff --git a/detector/__pycache__/rooftop_detection.cpython-38.pyc b/detector/__pycache__/rooftop_detection.cpython-38.pyc new file mode 100644 index 0000000..eace993 Binary files /dev/null and b/detector/__pycache__/rooftop_detection.cpython-38.pyc differ diff --git a/detector/get_map.py b/detector/get_map.py new file mode 100644 index 0000000..50164ae --- /dev/null +++ b/detector/get_map.py @@ -0,0 +1,30 @@ +import requests, urllib, json + +with open('keys.json', 'r') as f: + keys = json.load(f) + +maps_static_key = keys["maps-static"] + +imagewidth = 400 +imageheight = 400 +scale = 2 + +def get_map(zoom, latitude, longitude): + url = "https://maps.googleapis.com/maps/api/staticmap?" + center = str(latitude) + "," + str(longitude) + + urlparams = urllib.parse.urlencode({'center': center, + 'zoom': str(zoom), + 'size': str(imagewidth) + 'x' + str(imageheight), + 'maptype': 'satellite', + 'sensor': 'false', + 'scale': str(scale), + 'key': maps_static_key}) + print(url + urlparams) + r = requests.get(url + urlparams) + + if not (r.status_code==404 or r.status_code==403): + return r.content + else: + return 0 + diff --git a/detector/get_prediction.py b/detector/get_prediction.py new file mode 100644 index 0000000..72794e3 --- /dev/null +++ b/detector/get_prediction.py @@ -0,0 +1,76 @@ +import os, json, math +from google.cloud import automl_v1beta1 +from google.cloud.automl_v1beta1.proto import service_pb2 + +from detector import get_map as gm + +os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = "google_creds.json" + +with open('keys.json', 'r') as f: + keys = json.load(f) + +ml_project_id = keys["ml-project-id"] +ml_model_id = keys["ml-model-id"] + + +# 'content' is base-64-encoded image data. +def get_prediction(content, project_id, model_id): + prediction_client = automl_v1beta1.PredictionServiceClient() + + name = 'projects/{}/locations/us-central1/models/{}'.format(project_id, model_id) + payload = {'image': {'image_bytes': content }} + params = {} + request = prediction_client.predict(name, payload, params) + return request # waits till request is returned + +def calculateDistance(x1,y1,x2,y2): + dist = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) + return dist + +def make_recursive_prediction(zoom, latitude, longitude): + # Gets image of map from lat and long + img = gm.get_map(zoom, latitude, longitude) + + if (zoom > 18): + # Try running the following code without errors + try: + # make a prediction on the image + prediction = get_prediction(img, ml_project_id, ml_model_id) + + center = ((gm.imagewidth*gm.scale)/2,(gm.imageheight*gm.scale)/2) + dists = [] + + # iterate through each detection and calculate their distance to the center + for i in range (0, len(prediction.payload)): + detection = prediction.payload[i] + box = detection.image_object_detection.bounding_box.normalized_vertices + x1 = box[0].x*gm.imageheight*gm.scale + y1 = box[0].y*gm.imageheight*gm.scale + x2 = box[1].x*gm.imageheight*gm.scale + y2 = box[1].y*gm.imageheight*gm.scale + boxcenter = ((x1+x2)/2,(y1+y2)/2) + distToCenter = calculateDistance(boxcenter[0], boxcenter[1], center[0],center[1]) + dists.append((distToCenter,i)) + + # find the box closest to the center of the frame + closestDist = dists[0] + for box in dists: + if box[0] < closestDist[0]: + closestDist = box + + # return values of the closest box + payload = prediction.payload[closestDist[1]] + name = payload.display_name + score = payload.image_object_detection.score + box = payload.image_object_detection.bounding_box.normalized_vertices + x1 = box[0].x*gm.imageheight*gm.scale + y1 = box[0].y*gm.imageheight*gm.scale + x2 = box[1].x*gm.imageheight*gm.scale + y2 = box[1].y*gm.imageheight*gm.scale + return img, name, score, x1, y1, x2, y2, zoom + + # if no detections are found, zoom out and recursively run the function over again + except (IndexError, KeyError, TypeError): + zoom -= 1 + return make_recursive_prediction(zoom, latitude, longitude) + else: return 0 \ No newline at end of file diff --git a/detector/get_size.py b/detector/get_size.py new file mode 100644 index 0000000..68133ad --- /dev/null +++ b/detector/get_size.py @@ -0,0 +1,20 @@ +import math + +def get_roof_size(rType,x1,y1,x2,y2, latitude, zoom): + if zoom == 21: + metersPerPx = 2/46 + elif zoom == 20: + metersPerPx = 5/57 + elif zoom == 19: + metersPerPx = 10/57 + else: + metersPerPx = (156543.03392 * math.cos(latitude * math.pi / 180) / math.pow(2, zoom))/234 + + widthM = (x2-x1)*metersPerPx + lengthM = (y2-y1)*metersPerPx + area = widthM*lengthM + print(area, widthM, lengthM) + + return area + + diff --git a/detector/rooftop_detection.py b/detector/rooftop_detection.py new file mode 100644 index 0000000..990d5b9 --- /dev/null +++ b/detector/rooftop_detection.py @@ -0,0 +1,40 @@ +import cv2, base64 +import numpy as np + +from detector import get_size as gs +from detector import get_prediction as gp + +def draw_box(img, x1, y1, x2, y2): + start = (int(x1),int(y1)) + end = (int(x2),int(y2)) + image = np.asarray(bytearray(img), dtype="uint8") + image = cv2.imdecode(image, cv2.IMREAD_COLOR) + + cv2.rectangle(image, start, end, 0, 4) + #cv2.imshow("lalala", image) + #k = cv2.waitKey(0) + + b64img= cv2.imencode('.png', image)[1].tostring() + retval, buffer = cv2.imencode('.png', image) + png_as_text = base64.b64encode(buffer) + return png_as_text + +def get_roof_data(latitude, longitude): + zoom = 20 + image, name, score, x1, y1, x2, y2, endZoom = gp.make_recursive_prediction(zoom, latitude, longitude) + size = gs.get_roof_size(name, x1,y1,x2,y2, latitude, endZoom) + + image = draw_box(image, x1, y1, x2, y2) + + response = {} + response['image']=str(image)[2:-1] + response['name']=name + response['score']=score + response['size']=size + return response + +if __name__ == "__main__": + lat = 43.7322193485497874 + longi = -79.6181400203285 + + get_roof_data(lat, longi) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c3d9d67 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,28 @@ +cachetools==4.1.0 +certifi==2020.4.5.1 +chardet==3.0.4 +click==7.1.1 +Flask==1.1.2 +Flask-Cors==3.0.8 +google-api-core==1.17.0 +google-auth==1.14.1 +google-cloud==0.34.0 +google-cloud-automl==0.10.0 +googleapis-common-protos==1.51.0 +grpcio==1.28.1 +gunicorn==20.0.4 +idna==2.9 +itsdangerous==1.1.0 +Jinja2==2.11.2 +MarkupSafe==1.1.1 +numpy==1.18.3 +opencv-python==4.2.0.34 +protobuf==3.11.3 +pyasn1==0.4.8 +pyasn1-modules==0.2.8 +pytz==2019.3 +requests==2.23.0 +rsa==4.0 +six==1.14.0 +urllib3==1.25.9 +Werkzeug==1.0.1