source: issm/trunk-jpl/src/m/shp/shpread.py@ 27458

Last change on this file since 27458 was 27458, checked in by jdquinn, 2 years ago

CHG: MATLAB > Python translation; cleanup

File size: 5.5 KB
RevLine 
[26969]1import numpy as np
[25455]2from collections import OrderedDict
[25065]3from os import path
4try:
5 import shapefile
6except ImportError:
7 print("could not import shapefile, PyShp has not been installed, no shapefile reading capabilities enabled")
8
9from pairoptions import pairoptions
10
11
[27458]12def shpread(filename, *args): # {{{
[25455]13 """SHPREAD - read a shapefile and build a list of shapes
[25065]14
[26969]15 This routine reads a shapefile and builds a list of OrderedDict objects
16 containing the fields x and y corresponding to the coordinates, one for the
17 filename of the shp file, for the density, for the nodes, and a field
18 closed to indicate if the domain is closed. If this initial shapefile is
19 point only, the fields closed and points are ommitted. The first argument
20 is the shapefile to be read and the second argument (optional) indicates if
[25455]21 the last point shall be read (1 to read it, 0 not to).
[25065]22
23 The current implementation of shpread depends on PyShp.
24
25 Usage:
[25455]26 list = shpread(filename)
[25065]27
28 Example:
[26969]29 From underling PyShp implementation, "The shapefile format is actually
30 a collection of three files. You specify the base filename of the
31 shapefile or the complete filename of any of the shapefile component
[25455]32 files."
[25065]33
[25455]34 list = shpread('domainoutline.shp')
[25065]35 OR
[25455]36 list = shpread('domainoutline.dbf')
[25065]37 OR
[25455]38 list = shpread('domainoutline')
[25065]39
[26969]40 "OR any of the other 5+ formats which are potentially part of a
41 shapefile. The library does not care about file extensions". We do,
42 however, check that a file with the base filename or base filename with
[25065]43 .shp extension exists.
44
45 Sources:
46 - https://github.com/GeospatialPython/pyshp
[25125]47
[25455]48 NOTE:
[26969]49 - OrderedDict objects are used instead of OrderedStruct objects (although
50 addressing in the latter case is closer to the MATLAB struct type) in order
[25455]51 to remain consistent with the pattern established by src/m/exp/expread.py.
52
[25125]53 TODO:
[26969]54 - Create class that can be used to store and pretty print shape structs
[25455]55 (ala OrderedStruct from src/m/qmu/helpers.py).
[26969]56 - Convert returned data structure from list of OrderedDict objects to list
57 of OrderedStruct objects and remove corresponding note (see also
[25455]58 src/m/exp/expread.py). Also, modify handling of returned data structure in,
59 - src/m/classes/basin.py
60 - src/m/classes/boundary.py
61 - src/m/modules/ExpToLevelSet.py
62 - src/m/mesh/bamg.py
63 May also need to modify addressing in corresponding FetchData function, or
64 create new one, in src/wrappers/ContoursToNodes/ContoursToNodes.cpp.
65 """
[25065]66
67 #recover options
68 options = pairoptions(*args)
69
70 #some checks
71 if not (path.isfile(filename) or path.isfile(filename + '.shp')):
72 raise RuntimeError('shpread error message: file {} or {}.shp not found!'.format(filename, filename))
73
74 #read shapefile
75 sf = shapefile.Reader(filename)
76
[25125]77 Structs = []
[25065]78 shapes = sf.shapes()
[26969]79 for i, shape in enumerate(shapes):
[25455]80 Struct = OrderedDict()
[25065]81 if shape.shapeType == shapefile.POINT:
[25455]82 Struct['x'] = shape.points[0][0]
83 Struct['y'] = shape.points[0][1]
84 Struct['density'] = 1
85 Struct['Geometry'] = 'Point'
[25065]86 elif shape.shapeType == shapefile.POLYLINE:
87 num_points = len(shape.points)
88 x = []
89 y = []
[26969]90 for j, point in enumerate(shape.points):
[25065]91 x.append(point[0])
92 y.append(point[1])
[25455]93 Struct['x'] = x
94 Struct['y'] = y
95 Struct['nods'] = num_points
96 Struct['density'] = 1
97 Struct['closed'] = 1
98 Struct['BoundingBox'] = shape.bbox
99 Struct['Geometry'] = 'Line'
[25065]100 elif shape.shapeType == shapefile.POLYGON:
101 num_points = len(shape.points)
102 x = []
103 y = []
[26969]104 for j, point in enumerate(shape.points):
[25065]105 x.append(point[0])
106 y.append(point[1])
[25455]107 Struct['x'] = x
108 Struct['y'] = y
109 Struct['nods'] = num_points
110 Struct['density'] = 1
111 Struct['closed'] = 1
112 Struct['BoundingBox'] = shape.bbox
113 Struct['Geometry'] = 'Polygon'
[25065]114 else:
[26969]115 # NOTE: We could do this once before looping over shapes as all
116 # shapes in the file must be of the same type, but we would
117 # need to have a second check anyway in order to know how to
118 # parse the points. So, let's just assume the file is not
[25065]119 # malformed.
120 #
121 raise Exception('shpread error: geometry {} is not currently supported'.format(shape.shapeTypeName))
[25125]122
[25065]123 name = ''
124 fields = sf.fields
[26969]125 for j in range(1, len(fields)): # skip over first field, which is "DeletionFlag"
[25065]126 fieldname = fields[j][0]
127 # 'id' field gets special treatment
[26969]128 if fieldname in ['id', 'fid']:
129 name = str(sf.record(i)[j - 1]) # record index is offset by one, again, because of "DeletionFlag"
[25065]130 else:
[26969]131 setattr(Struct, str(fieldname), sf.record(i)[j - 1]) # cast to string removes "u" from "u'fieldname'"
[25455]132 Struct['name'] = name
[25125]133 Structs.append(Struct)
[25065]134
135 invert = options.getfieldvalue('invert', 0)
136 if invert:
[25125]137 for i in range(len(Structs)):
[25455]138 Structs[i]['x'] = np.flipud(Structs[i]['x'])
139 Structs[i]['y'] = np.flipud(Structs[i]['y'])
[25065]140
[25125]141 return Structs
[27458]142# }}}
Note: See TracBrowser for help on using the repository browser.