diff --git a/src/Mod/Arch/CMakeLists.txt b/src/Mod/Arch/CMakeLists.txt index eaf9d3931b..151071742d 100644 --- a/src/Mod/Arch/CMakeLists.txt +++ b/src/Mod/Arch/CMakeLists.txt @@ -53,6 +53,7 @@ SET(Arch_SRCS exportIFC.py ArchTruss.py ArchCurtainWall.py + importSHP.py ) SET(Dice3DS_SRCS diff --git a/src/Mod/Arch/Init.py b/src/Mod/Arch/Init.py index 0e292db883..884d62d40d 100644 --- a/src/Mod/Arch/Init.py +++ b/src/Mod/Arch/Init.py @@ -30,3 +30,4 @@ FreeCAD.addImportType("Collada (*.dae)","importDAE") FreeCAD.addExportType("Collada (*.dae)","importDAE") FreeCAD.addImportType("3D Studio mesh (*.3ds)","import3DS") FreeCAD.addImportType("SweetHome3D XML export (*.zip)","importSH3D") +FreeCAD.addImportType("Shapefile (*.shp)","importSHP") diff --git a/src/Mod/Arch/importSHP.py b/src/Mod/Arch/importSHP.py new file mode 100644 index 0000000000..88786aaf2b --- /dev/null +++ b/src/Mod/Arch/importSHP.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +#*************************************************************************** +#* * +#* Copyright (c) 2020 Yorik van Havre * +#* * +#* 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 * +#* * +#*************************************************************************** + +from __future__ import print_function + +import os +import FreeCAD +import importIFCHelper +translate = FreeCAD.Qt.translate + +if open.__module__ in ['__builtin__','io']: + pythonopen = open + +def open(filename): + + """opens a SHP/SHX/DBF file in a new FreeCAD document""" + + docname = os.path.splitext(os.path.basename(filename))[0] + docname = importIFCHelper.decode(docname,utf=True) + doc = FreeCAD.newDocument(docname) + doc.Label = docname + doc = insert(filename,doc.Name) + return doc + + +def insert(filename,docname,record=None): + + """imports a SHP/SHX/DBF file in an existing FreeCAD document. + the record attribute is an optional string indicating the shapefile + field to use to give elevations to the different shapes. If not used, + if running in GUI mode, a dialog will pop up to ask the user which + field to use.""" + + if not checkShapeFileLibrary(): + return + + import shapefile + import Part + + # read the shape file + # doc at https://github.com/GeospatialPython/pyshp + + shp = shapefile.Reader(filename) + + # check which record to use for elevation + if not record: + fields = ["None"] + [field[0] for field in shp.fields] + if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtGui + reply = QtGui.QInputDialog.getItem(FreeCADGui.getMainWindow(), + translate("Arch","Shapes elevation"), + translate("Arch","Choose which field provides shapes elevations:"), + fields) + if reply[1]: + if record != "None": + record = reply[0] + + # build shapes + shapes = [] + for shaperec in shp.shapeRecords(): + shape = None + pts = [] + for p in shaperec.shape.points: + if len(p) > 2: + pts.append(FreeCAD.Vector(p[0],p[1],p[2])) + else: + pts.append(FreeCAD.Vector(p[0],p[1],0)) + if shp.shapeTypeName in ["POLYGON","POLYGONZ"]: + # faces + pts.append(pts[0]) + shape = Part.makePolygon(pts) + shape = Part.Face(shape) + elif shp.shapeTypeName in ["POINT","POINTZ"]: + # points + verts = [Part.Vertex(p) for p in pts] + if verts: + shape = Part.makeCompound(verts) + else: + # polylines + shape = Part.makePolygon(pts) + if record: + elev = shaperec.record[record] + if elev: + shape.translate(FreeCAD.Vector(0,0,elev)) + if shape: + shapes.append(shape) + if shapes: + result = Part.makeCompound(shapes) + obj = FreeCAD.ActiveDocument.addObject("Part::Feature","shapefile") + obj.Shape = result + obj.Label = os.path.splitext(os.path.basename(filename))[0] + FreeCAD.ActiveDocument.recompute() + else: + FreeCAD.Console.PrintWarning(translate("Arch","No shape found in this file")+"\n") + +def getFields(filename): + + """returns the fields found in the given file""" + + if not checkShapeFileLibrary(): + return + import shapefile + shp = shapefile.Reader(filename) + return [field[0] for field in shp.fields] + +def checkShapeFileLibrary(): + + """Looks for and/or installs the ShapeFile library""" + + try: + import shapefile + except: + url = "https://raw.githubusercontent.com/GeospatialPython/pyshp/master/shapefile.py" + if FreeCAD.GuiUp: + import addonmanager_utilities + import FreeCADGui + from PySide import QtGui + reply = QtGui.QMessageBox.question(FreeCADGui.getMainWindow(), + translate("Arch","Shapefile module not found"), + translate("Arch","The shapefile python library was not found on your system. Would you like to downloadit now from https://github.com/GeospatialPython/pyshp? It will be placed in your macros folder."), + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, + QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.Yes: + u = addonmanager_utilities.urlopen(url) + if not u: + FreeCAD.Console.PrintError(translate("Arch","Error: Unable to download from:")+" "+url+"\n") + return False + b = u.read() + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro") + fp = p.GetString("MacroPath",os.path.join(FreeCAD.getUserAppDataDir(),"Macros")) + fp = os.path.join(fp,"shapefile.py") + f = pythonopen(fp,"wb") + f.write(b) + f.close() + try: + import shapefile + except: + FreeCAD.Console.PrintError(translate("Arch","Could not download shapefile module. Aborting.")+"\n") + return False + else: + FreeCAD.Console.PrintError(translate("Arch","Shapefile module not downloaded. Aborting.")+"\n") + return False + else: + FreeCAD.Console.PrintError(translate("Arch","Shapefile module not found. Aborting.")+"\n") + FreeCAD.Console.PrintMessage(translate("Arch","The shapefile library can be downloaded from the following URL and installed in your macros folder:")+"\n") + FreeCAD.Console.PrintMessage(url) + return False + return True