# /********************************************************************** # * * # * Copyright (c) 2021 Javier Braña * # * * # * This program is free software; you can redistribute it and/or modify* # * it under the terms of the GNU Lesser General Public License (LGPL) * # * as published by the Free Software Foundation; either version 2 of * # * the License, or (at your option) any later version. * # * for detail see the LICENCE text file. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU Library General Public License for more details. * # * * # * You should have received a copy of the GNU Library General Public * # * License along with this program; if not, write to the Free Software * # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307* # * USA * # * * # *********************************************************************** import FreeCAD import utm if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui from PySide.QtCore import QT_TRANSLATE_NOOP from PySide2.QtWebEngineWidgets import QWebEngineView from PySide2.QtWebChannel import QWebChannel import os else: # \cond def translate(ctxt,txt): return txt def QT_TRANSLATE_NOOP(ctxt,txt): return txt # \endcond import PVPlantResources from PVPlantResources import DirIcons as DirIcons from PVPlantResources import DirResources as DirResources class MapWindow(QtGui.QWidget): def __init__(self, WinTitle="MapWindow"): super(MapWindow, self).__init__() self.raise_() self.lat = 0 self.lon = 0 self.WinTitle = WinTitle self.setupUi() def setupUi(self): self.ui = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantGeoreferencing.ui", self) self.resize(1200, 800) self.setWindowTitle(self.WinTitle) self.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "Location.svg"))) self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) self.layout = QtGui.QHBoxLayout(self) self.layout.setContentsMargins(4, 4, 4, 4) LeftWidget = QtGui.QWidget(self) LeftLayout = QtGui.QVBoxLayout(LeftWidget) LeftWidget.setLayout(LeftLayout) LeftLayout.setContentsMargins(0, 0, 0, 0) RightWidget = QtGui.QWidget(self) RightWidget.setFixedWidth(350) RightLayout = QtGui.QVBoxLayout(RightWidget) RightWidget.setLayout(RightLayout) RightLayout.setContentsMargins(0, 0, 0, 0) self.layout.addWidget(LeftWidget) self.layout.addWidget(RightWidget) # Left Widgets: # -- Search Bar: self.valueSearch = QtGui.QLineEdit(self) self.valueSearch.setPlaceholderText("Search") self.valueSearch.returnPressed.connect(self.onSearch) searchbutton = QtGui.QPushButton('Search') searchbutton.setFixedWidth(80) searchbutton.clicked.connect(self.onSearch) SearchBarLayout = QtGui.QHBoxLayout(self) SearchBarLayout.addWidget(self.valueSearch) SearchBarLayout.addWidget(searchbutton) LeftLayout.addLayout(SearchBarLayout) # -- Webbroser: self.view = QWebEngineView() self.channel = QWebChannel(self.view.page()) self.view.page().setWebChannel(self.channel) self.channel.registerObject("MyApp", self) file = os.path.join(DirResources, "webs", "main.html") self.view.page().loadFinished.connect(self.onLoadFinished) self.view.page().load(QtCore.QUrl.fromLocalFile(file)) LeftLayout.addWidget(self.view) # self.layout.addWidget(self.view, 1, 0, 1, 3) # -- Latitud y longitud: self.labelCoordinates = QtGui.QLabel() self.labelCoordinates.setFixedHeight(21) LeftLayout.addWidget(self.labelCoordinates) # self.layout.addWidget(self.labelCoordinates, 2, 0, 1, 3) # Right Widgets: labelKMZ = QtGui.QLabel() labelKMZ.setText("Cargar un archivo KMZ/KML:") self.kmlButton = QtGui.QPushButton() self.kmlButton.setFixedSize(32, 32) self.kmlButton.setIcon(QtGui.QIcon(os.path.join(DirIcons, "googleearth.svg"))) widget = QtGui.QWidget(self) layout = QtGui.QHBoxLayout(widget) widget.setLayout(layout) layout.addWidget(labelKMZ) layout.addWidget(self.kmlButton) RightLayout.addWidget(widget) # ----------------------- self.groupbox = QtGui.QGroupBox("Importar datos desde:") self.groupbox.setCheckable(True) self.groupbox.setChecked(True) radio1 = QtGui.QRadioButton("Google Elevation") radio2 = QtGui.QRadioButton("Nube de Puntos") radio3 = QtGui.QRadioButton("Datos GPS") radio1.setChecked(True) # buttonDialog = QtGui.QPushButton('...') # buttonDialog.setEnabled(False) vbox = QtGui.QVBoxLayout(self) vbox.addWidget(radio1) vbox.addWidget(radio2) vbox.addWidget(radio3) self.groupbox.setLayout(vbox) RightLayout.addWidget(self.groupbox) # ------------------------ verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) RightLayout.addItem(verticalSpacer) self.bAccept = QtGui.QPushButton('Accept') self.bAccept.clicked.connect(self.onAcceptClick) RightLayout.addWidget(self.bAccept) # signals/slots QtCore.QObject.connect(self.kmlButton, QtCore.SIGNAL("clicked()"), self.importKML) def onLoadFinished(self): file = os.path.join(DirResources, "webs", "map.js") frame = self.view.page() with open(file, 'r') as f: frame.runJavaScript(f.read()) def onSearch(self): if self.valueSearch.text() == "": return from geopy.geocoders import Nominatim geolocator = Nominatim(user_agent="http") location = geolocator.geocode(self.valueSearch.text()) print(location.raw) self.valueSearch.setText(location.address) self.panMap(location.longitude, location.latitude, location.raw['boundingbox']) def onAcceptClick(self): frame = self.view.page() # 1. georeferenciar frame.runJavaScript( "MyApp.georeference(drawnItems.getBounds().getCenter().lat, drawnItems.getBounds().getCenter().lng);" ) # 2. importar todos los elementos dibujados: frame.runJavaScript( "var data = drawnItems.toGeoJSON();" "MyApp.shapes(JSON.stringify(data));" ) self.close() @QtCore.Slot(float, float) def onMapMove(self, lat, lng): self.lat = lat self.lon = lng x, y, zone_number, zone_letter = utm.from_latlon(lat, lng) self.labelCoordinates.setText('Longitud: {:.5f}, Latitud: {:.5f}'.format(lng, lat) + ' | UTM: ' + str(zone_number) + zone_letter + ', {:.5f}m E, {:.5f}m N'.format(x, y)) @QtCore.Slot(float, float) def georeference(self, lat, lng): import PVPlantSite from geopy.geocoders import Nominatim Site = PVPlantSite.get(create=True) Site.Proxy.setLatLon(lat, lng) geolocator = Nominatim(user_agent="http") location = geolocator.reverse('{:.5f}, {:.5f}'.format(lat, lng)) if location: if location.raw["address"].get("road"): str = location.raw["address"]["road"] if location.raw["address"].get("house_number"): str += ' ({0})'.format(location.raw["address"]["house_number"]) Site.Address = str if location.raw["address"].get("city"): Site.City = location.raw["address"]["city"] if location.raw["address"].get("postcode"): Site.PostalCode = location.raw["address"]["postcode"] if location.raw["address"].get("address"): Site.Region = '{0}'.format(location.raw["address"]["province"]) if location.raw["address"].get("state"): if Site.Region != "": Site.Region += " - " Site.Region += '{0}'.format(location.raw["address"]["state"]) # province - state Site.Country = location.raw["address"]["country"] @QtCore.Slot(str) def shapes(self, drawnItems): import geojson import PVPlantImportGrid as ImportElevation import Draft items = geojson.loads(drawnItems) for item in items['features']: if item['geometry']['type'] == "Point": # 1. if the feature is a Point or Circle: coord = item['geometry']['coordinates'] point = ImportElevation.getElevationFromOE([[coord[0], coord[1]],]) c = FreeCAD.Vector(point[0][0], point[0][1], point[0][2]) if item['properties'].get('radius'): r = round(item['properties']['radius'] * 1000, 0) p = FreeCAD.Placement() p.Base = c obj = Draft.makeCircle(r, placement=p, face=False) else: ''' do something ''' obj = Draft.make_point(c * 1000, color=(0.5, 0.3, 0.6), point_size=10) else: # 2. if the feature is a Polygon or Line: cw = False name = "Línea" lp = item['geometry']['coordinates'] if item['geometry']['type'] == "Polygon": cw = True name = "Area" lp = item['geometry']['coordinates'][0] pts = [] for cords in lp: pts.append([cords[1], cords[0]]) tmp = ImportElevation.getElevationFromOE(pts) pts = [] for p in tmp: pts.append(p.sub(FreeCAD.ActiveDocument.Site.Origin)) obj = Draft.makeWire(pts, closed=cw, face=False) obj.Placement.Base = FreeCAD.ActiveDocument.Site.Origin obj.Label = name Draft.autogroup(obj) if item['properties'].get('name'): obj.Label = item['properties']['name'] FreeCAD.activeDocument().recompute() FreeCADGui.updateGui() FreeCADGui.SendMsgToActiveView("ViewFit") def panMap(self, lng, lat, geometry=""): frame = self.view.page() bbox = "[{0}, {1}], [{2}, {3}]".format(float(geometry[0]), float(geometry[2]), float(geometry[1]), float(geometry[3])) command = 'map.panTo(L.latLng({lt}, {lg}));'.format(lt=lat, lg=lng) command += 'map.fitBounds([{box}]);'.format(box=bbox) frame.runJavaScript(command) def importKML(self): file = QtGui.QFileDialog.getOpenFileName(None, "FileDialog", "", "Google Earth (*.kml *.kmz)")[0] from lib.kml2geojson import kmz_convert layers = kmz_convert(file, "", ) frame = self.view.page() for layer in layers: command = "drawnItems.addLayer(L.geoJSON({0}));".format(layer) frame.runJavaScript(command) class _CommandPVPlantGeoreferencing: def GetResources(self): return {'Pixmap': str(os.path.join(DirIcons, "Location.svg")), 'Accel': "G, R", 'MenuText': QT_TRANSLATE_NOOP("Georeferencing","Georeferencing"), 'ToolTip': QT_TRANSLATE_NOOP("Georeferencing","Referenciar el lugar")} def Activated(self): self.form = MapWindow() self.form.show() def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False if FreeCAD.GuiUp: FreeCADGui.addCommand('PVPlantGeoreferencing',_CommandPVPlantGeoreferencing())