diff --git a/Importer/importOSM.py b/Importer/importOSM.py new file mode 100644 index 0000000..3113640 --- /dev/null +++ b/Importer/importOSM.py @@ -0,0 +1,90 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 microelly <> * +# * Copyright (c) 2020 Bernd Hahnebach * +# * Copyright (c) 2022 Hakan Seven * +# * * +# * 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 data from OpenStreetMap +""" + +import FreeCAD +import Part +from xml.etree import ElementTree as ET +import urllib.request +import urllib.parse + +def import_osm_data(min_lat=40.41, min_lon=-3.71, max_lat=40.42, max_lon=-3.7): + # Configurar la consulta de Overpass API + overpass_url = "https://overpass-api.de/api/interpreter" + + query = f""" + [out:xml]; + ( + node({min_lat},{min_lon},{max_lat},{max_lon}); + way({min_lat},{min_lon},{max_lat},{max_lon}); + ); + out body; + >; + out skel qt; + """ + + print("Descargando datos de OSM...") + response = urllib.request.urlopen(overpass_url, data=query.encode('utf-8')) + osm_data = response.read() + + print("Procesando datos...") + root = ET.fromstring(osm_data) + nodes = {} + + if not FreeCAD.ActiveDocument: + FreeCAD.newDocument() + doc = FreeCAD.ActiveDocument + + # Creación de nodos corregida + for node in root.findall('node'): + node_id = node.attrib['id'] + lat = float(node.attrib['lat']) + lon = float(node.attrib['lon']) + nodes[node_id] = FreeCAD.Vector(lon, lat, 0) + + # Versión estable para todas las versiones de FreeCAD + point = Part.Vertex(nodes[node_id]) + Part.show(point, f"Node_{node_id}") + + # Procesar vías (manera alternativa) + for way in root.findall('way'): + way_points = [nodes[nd.attrib['ref']] + for nd in way.findall('nd') + if nd.attrib['ref'] in nodes] + + if len(way_points) > 1: + try: + wire = Part.makePolygon(way_points) + Part.show(wire, f"Way_{way.attrib['id']}") + except Exception as e: + print(f"Error en vía {way.attrib['id']}: {str(e)}") + + print("Importación completada!") + FreeCAD.ActiveDocument.recompute() + +# Ejecutar la función +import_osm_data() \ No newline at end of file diff --git a/InitGui.py b/InitGui.py index cdce3c3..be8a351 100644 --- a/InitGui.py +++ b/InitGui.py @@ -20,53 +20,62 @@ # * * # *********************************************************************** -__title__="FreeCAD Fotovoltaic Power Plant Toolkit" +__title__ = "FreeCAD Fotovoltaic Power Plant Toolkit" __author__ = "Javier Braña" __url__ = "sn" +from pathlib import Path +import sys +import os +import FreeCADGui + +FreeCADGui.updateLocale() + +try: + import PVPlantGeoreferencing, PVPlantPlacement, \ + PVPlantTerrainAnalisys, PVPlantSite, PVPlantImportGrid, PVPlantFence, \ + PVPlantFoundation, PVPlantCreateTerrainMesh, \ + PVPlantTreeGenerator, PVPlantBuilding, PVPlantTrench, PVPlantEarthWorks, \ + PVPlantStringing, \ + PVPlantPad, PVPlantRoad, PVPlantTerrain, PVPlantManhole, \ + GraphProfile, Utils.PVPlantTrace, \ + reload + import PVPlantRackChecking + + from Project.Area import PVPlantArea, PVPlantAreaUtils + from Project import ProjectSetup + + from Importer import importDXF + + from Mechanical.Frame import PVPlantFrame + + from Electrical.Cable import PVPlantCable, PVPlantElectricalLine + from Electrical.CombinerBox import PVPlantStringBox + from Electrical.Inverter import PVPlantInverter +except ImportError as e: + print(f"Error importing modules: {e}") -class PVPlantWorkbench (Workbench): - import os +class PVPlantWorkbench(Workbench): from PVPlantResources import DirIcons as DirIcons - MenuText = "PVPlant" ToolTip = "Workbench for PV design" Icon = str(os.path.join(DirIcons, "icon.svg")) def Initialize(self): - import sys - sys.path.append(r"C:\Users\javie\AppData\Roaming\FreeCAD\Mod") - # Mias - import PVPlantGeoreferencing, PVPlantPlacement, \ - PVPlantTerrainAnalisys, PVPlantSite, PVPlantImportGrid, PVPlantFence,\ - PVPlantFoundation, PVPlantCreateTerrainMesh, \ - PVPlantTreeGenerator, PVPlantBuilding, PVPlantTrench, PVPlantEarthWorks, \ - PVPlantStringing, \ - PVPlantPad, PVPlantRoad, PVPlantTerrain, PVPlantManhole, \ - GraphProfile, Utils.PVPlantTrace,\ - reload - import PVPlantRackChecking + #sys.path.append(r"C:\Users\javie\AppData\Roaming\FreeCAD\Mod") + sys.path.append(os.path.join(FreeCAD.getUserAppDataDir(), 'Mod')) + import PVPlantTools - from Project.Area import PVPlantArea, PVPlantAreaUtils - from Project import ProjectSetup - from Export import exportPVSyst, PVPlantBOQMechanical, PVPlantBOQElectrical, PVPlantBOQCivil,\ - exportDXF - from Importer import importDXF - - from Mechanical.Frame import PVPlantFrame - - from Electrical.Cable import PVPlantCable, PVPlantElectricalLine - from Electrical.CombinerBox import PVPlantStringBox - from Electrical.Inverter import PVPlantInverter + self.projectlist = PVPlantTools.projectlist # A list of command names created in the line above - self.projectlist = ["Reload", + '''self.projectlist = ["Reload", "PVPlantSite", "PVPlantGeoreferencing", "ProjectSetup", - #"ImportGrid", + # "ImportGrid", "Terrain", "PointsGroup", "PVPlantCreateTerrainMesh", @@ -78,35 +87,28 @@ class PVPlantWorkbench (Workbench): "PVPlantPad", "PVPlantRoad", "PVPlantManhole", - #"PVPlantFoundation" + # "PVPlantFoundation" "GraphTerrainProfile", "Trace", - ] + ]''' self.framelist = [ - "RackType", - "PVPlantRackCheck", - "Separator", - "PVPlantPlacement", - "PVPlantAdjustToTerrain", - "PVPlantConvertTo", - "PVArea" - ] + "RackType", + "PVPlantRackCheck", + "Separator", + "PVPlantPlacement", + "PVPlantAdjustToTerrain", + "PVPlantConvertTo", + "PVArea" + ] self.objectlist = [ - "PVPlantTree", - "PVPlantBuilding", - "PVPlantFenceGroup", - ] - - self.inportExportlist = ["BOQCivil", - "BOQMechanical", - "BOQElectrical", - "Separator", - "exportDXF", - #"importDXF", - "ExportToPVSyst", - ] + "PVPlantTree", + "PVPlantBuilding", + "PVPlantFenceGroup", + ] + from Export import ExporterCommands + self.inportExportlist = ExporterCommands.Exportlist self.electricalList = ["PVPlantStringBox", "PVPlantCable", "PVPlanElectricalLine", @@ -114,14 +116,14 @@ class PVPlantWorkbench (Workbench): "Stringing", "Separator", "StringInverter", - ] + ] self.roads = ["PVPlantRoad", - ] + ] - self.pads = ["PVPlantPad", - "Separator" + self.pads = ["PVPlantPad", + "Separator" ] # Toolbar @@ -140,34 +142,36 @@ class PVPlantWorkbench (Workbench): # Draft tools from DraftTools import translate - self.drafttools = ["Draft_Line","Draft_Wire","Draft_Circle","Draft_Arc","Draft_Ellipse", - "Draft_Polygon","Draft_Rectangle", "Draft_Text", - "Draft_Dimension", "Draft_BSpline","Draft_Point", - "Draft_Facebinder","Draft_BezCurve","Draft_Label"] - self.draftmodtools = ["Draft_Move","Draft_Rotate","Draft_Offset", - "Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale", - "Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array", - "Draft_Clone"] - self.draftextratools = ["Draft_WireToBSpline","Draft_ShapeString", - "Draft_PathArray","Draft_Mirror","Draft_Stretch"] - self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup","Draft_AutoGroup", - "Draft_SelectGroup","Draft_SelectPlane", - "Draft_ShowSnapBar","Draft_ToggleGrid",] - self.draftutils = ["Draft_Heal","Draft_FlipDimension", - "Draft_ToggleConstructionMode","Draft_ToggleContinueMode","Draft_Edit", - "Draft_Slope","Draft_AddConstruction"] - self.snapList = ['Draft_Snap_Lock','Draft_Snap_Midpoint','Draft_Snap_Perpendicular', - 'Draft_Snap_Grid','Draft_Snap_Intersection','Draft_Snap_Parallel', - 'Draft_Snap_Endpoint','Draft_Snap_Angle','Draft_Snap_Center', - 'Draft_Snap_Extension','Draft_Snap_Near','Draft_Snap_Ortho','Draft_Snap_Special', - 'Draft_Snap_Dimensions','Draft_Snap_WorkingPlane'] - + self.drafttools = ["Draft_Line", "Draft_Wire", "Draft_Circle", "Draft_Arc", "Draft_Ellipse", + "Draft_Polygon", "Draft_Rectangle", "Draft_Text", + "Draft_Dimension", "Draft_BSpline", "Draft_Point", + "Draft_Facebinder", "Draft_BezCurve", "Draft_Label"] + self.draftmodtools = ["Draft_Move", "Draft_Rotate", "Draft_Offset", + "Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale", + "Draft_Shape2DView", "Draft_Draft2Sketch", "Draft_Array", + "Draft_Clone"] + self.draftextratools = ["Draft_WireToBSpline", "Draft_ShapeString", + "Draft_PathArray", "Draft_Mirror", "Draft_Stretch"] + self.draftcontexttools = ["Draft_ApplyStyle", "Draft_ToggleDisplayMode", "Draft_AddToGroup", "Draft_AutoGroup", + "Draft_SelectGroup", "Draft_SelectPlane", + "Draft_ShowSnapBar", "Draft_ToggleGrid", ] + self.draftutils = ["Draft_Heal", "Draft_FlipDimension", + "Draft_ToggleConstructionMode", "Draft_ToggleContinueMode", "Draft_Edit", + "Draft_Slope", "Draft_AddConstruction"] + self.snapList = ['Draft_Snap_Lock', 'Draft_Snap_Midpoint', 'Draft_Snap_Perpendicular', + 'Draft_Snap_Grid', 'Draft_Snap_Intersection', 'Draft_Snap_Parallel', + 'Draft_Snap_Endpoint', 'Draft_Snap_Angle', 'Draft_Snap_Center', + 'Draft_Snap_Extension', 'Draft_Snap_Near', 'Draft_Snap_Ortho', 'Draft_Snap_Special', + 'Draft_Snap_Dimensions', 'Draft_Snap_WorkingPlane'] def QT_TRANSLATE_NOOP(scope, text): return text + self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Draft tools"), self.drafttools) self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Draft mod tools"), self.draftmodtools) - self.appendMenu(QT_TRANSLATE_NOOP("arch", "&Draft"), self.drafttools + self.draftmodtools + self.draftextratools) - self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Utilities")], self.draftutils + self.draftcontexttools) + self.appendMenu(QT_TRANSLATE_NOOP("arch", "&Draft"), + self.drafttools + self.draftmodtools + self.draftextratools) + self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Utilities")], + self.draftutils + self.draftcontexttools) self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Snapping")], self.snapList) import Part @@ -205,10 +209,10 @@ class PVPlantWorkbench (Workbench): "This is executed whenever the user right-clicks on screen" # "recipient" will be either "view" or "tree" - #if FreeCAD.activeDraftCommand is None: + # if FreeCAD.activeDraftCommand is None: if recipient.lower() == "view": print("Menus en la 'View'") - #if FreeCAD.activeDraftCommand is None: + # if FreeCAD.activeDraftCommand is None: presel = FreeCADGui.Selection.getPreselection() print(presel.SubElementNames, " - ", presel.PickedPoints) if not presel is None: @@ -239,7 +243,7 @@ class PVPlantWorkbench (Workbench): self.contextMenu.show() ''' - def GetClassName(self): + def GetClassName(self): # this function is mandatory if this is a full python workbench return "Gui::PythonWorkbench" diff --git a/Photovoltaic/Module.py b/Photovoltaic/Module.py new file mode 100644 index 0000000..b338f8e --- /dev/null +++ b/Photovoltaic/Module.py @@ -0,0 +1,301 @@ +# /********************************************************************** +# * * +# * 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 ArchComponent +import FreeCAD +import Part + +if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtCore + import draftguitools.gui_trackers as DraftTrackers +else: + # \cond + def translate(ctxt, txt): + return txt + def QT_TRANSLATE_NOOP(ctxt, txt): + return txt + # \endcond + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +import os +import PVPlantResources +from PVPlantResources import DirIcons as DirIcons + + +def makeModule(name="Module"): + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name) + obj.Label = name + Module(obj) + ViewProviderModule(obj.ViewObject) + FreeCAD.ActiveDocument.recompute() + return obj + + +class Module(ArchComponent.Component): + "A Manhole Obcject" + + def __init__(self, obj): + # Definición de Variables: + ArchComponent.Component.__init__(self, obj) + self.setProperties(obj) + + def setProperties(self, obj): + pl = obj.PropertiesList + # Base de datos: ----------------------------------------------------------------------------------------------- + if not ("Manufacturer" in pl): + obj.addProperty("App::PropertyStringList", + "Manufacturer", + "Module", + "The manufacturer of this object" + ) + + if not ("Model" in pl): + obj.addProperty("App::PropertyStringList", + "Model", + "Module", + "The model of this object" + ) + + # Dimensions: -------------------------------------------------------------------------------------------------- + if not ("Height" in pl): + obj.addProperty("App::PropertyLength", + "Height", + "Manhole", + "The height of this object" + ).Height = 2804 + + if not ("Width" in pl): + obj.addProperty("App::PropertyLength", + "Width", + "Manhole", + "The width of this object" + ).Width = 1303 + + if not ("Thickness" in pl): + obj.addProperty("App::PropertyLength", + "Thickness", + "Manhole", + "The height of this object" + ).Thickness = 35 + + # Electrical: -------------------------------------------------------------------------------------------------- + if not ("Power" in pl): + obj.addProperty("App::PropertyPower", + "Power", + "Outputs", + "The height of this object" + ).Power = 650 + + if not ("Umpp" in pl): + obj.addProperty("App::PropertyElectricCurrent", + "Umpp", + "Outputs", + "The height of this object" + ).Umpp = 650 + + if not ("Impp" in pl): + obj.addProperty("App::PropertyElectricCurrent", + "Impp", + "Outputs", + "The height of this object" + ).Impp = 650 + + if not ("Uoc" in pl): + obj.addProperty("App::PropertyElectricCurrent", + "Uoc", + "Outputs", + "The height of this object" + ).Uoc = 650 + + if not ("Isc" in pl): + obj.addProperty("App::PropertyElectricCurrent", + "Isc", + "Outputs", + "The height of this object" + ).Isc = 650 + + if not ("Isc" in pl): + obj.addProperty("App::PropertyElectricCurrent", + "Impp", + "Outputs", + "The height of this object" + ).Impp = 650 + + self.Type = "Module" + obj.Proxy = self + + def onDocumentRestored(self, obj): + """Method run when the document is restored. + Re-adds the Arch component, and Arch wall properties.""" + ArchComponent.Component.onDocumentRestored(self, obj) + self.setProperties(obj) + + def onChanged(self, obj, prop): + '''Do something when a property has changed''' + + def execute(self, obj): + box = Part.makeBox(obj.Width, obj.Height, obj.Thickness) + box.translate(FreeCAD.Vector(-obj.Width, -obj.Height, 0) / 2) + obj.Shape = box + + +class ViewProviderModule(ArchComponent.ViewProviderComponent): + "A View Provider for the Module object" + + def __init__(self, vobj): + ArchComponent.ViewProviderComponent.__init__(self, vobj) + + def getIcon(self): + return str(os.path.join(DirIcons, "manhole.svg")) + + def setEdit(self, vobj, mode): + """Method called when the document requests the object to enter edit mode. + + Edit mode is entered when a user double clicks on an object in the tree + view, or when they use the menu option [Edit -> Toggle Edit Mode]. + + Just display the standard Arch component task panel. + + Parameters + ---------- + mode: int or str + The edit mode the document has requested. Set to 0 when requested via + a double click or [Edit -> Toggle Edit Mode]. + + Returns + ------- + bool + If edit mode was entered. + """ + + if (mode == 0) and hasattr(self, "Object"): + taskd = _ManholeTaskPanel(self.Object) + taskd.obj = self.Object + # taskd.update() + FreeCADGui.Control.showDialog(taskd) + return True + + return False + + +class ManholeTaskPanel: + def __init__(self, obj=None): + self.new = False + if obj is None: + self.new = True + obj = makeManhole() + + self.obj = obj + + self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantManhole.ui") + + self.node = None + self.view = FreeCADGui.ActiveDocument.ActiveView + self.tracker = DraftTrackers.ghostTracker(obj) + self.tracker.on() + self.call = self.view.addEventCallback("SoEvent", self.action) + + def action(self, arg): + """Handle the 3D scene events. + + This is installed as an EventCallback in the Inventor view. + + Parameters + ---------- + arg: dict + Dictionary with strings that indicates the type of event received + from the 3D view. + """ + + if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE": + self.finish() + + elif arg["Type"] == "SoLocation2Event": + point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) + if info: + self.tracker.move(FreeCAD.Vector(info["x"], info["y"], info["z"])) + else: + self.tracker.move(point) + + elif (arg["Type"] == "SoMouseButtonEvent" and + arg["State"] == "DOWN" and + arg["Button"] == "BUTTON1"): + + point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) + if info: + self.obj.Placement.Base = FreeCAD.Vector(info["x"], info["y"], info["z"]) + else: + self.obj.Placement.Base = point + self.finish() + + def finish(self): + self.accept() + + def accept(self): + self.closeForm() + return True + + def reject(self): + if self.new: + FreeCAD.ActiveDocument.removeObject(self.obj.Name) + self.closeForm() + return True + + def closeForm(self): + self.tracker.finalize() + FreeCADGui.Control.closeDialog() + self.view.removeEventCallback("SoEvent", self.call) + + +'''class _CommandManhole: + "the Arch Building command definition" + + def GetResources(self): + return {'Pixmap': str(os.path.join(DirIcons, "manhole.svg")), + 'MenuText': "Manhole", + 'Accel': "C, M", + 'ToolTip': "Creates a Manhole object from setup dialog."} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + if FreeCAD.ActiveDocument is not None: + if FreeCADGui.Selection.getCompleteSelection(): + for ob in FreeCAD.ActiveDocument.Objects: + if ob.Name[:4] == "Site": + return True + + def Activated(self): + TaskPanel = _ManholeTaskPanel() + FreeCADGui.Control.showDialog(TaskPanel) + return + + +if FreeCAD.GuiUp: + FreeCADGui.addCommand('PVPlantManhole', _CommandManhole())''' +