-
Notifications
You must be signed in to change notification settings - Fork 0
/
shputil.py
executable file
·116 lines (96 loc) · 3.58 KB
/
shputil.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python
import click
import shapefile
from tqdm import tqdm
@click.group()
def shp_util():
pass
def _iterRecords(sfreader, title='Reading records'):
return tqdm(sfreader.iterRecords(), title, unit=' records', total=len(sfreader))
def _iterShapeRecords(sfreader, title='Reading records'):
return tqdm(sfreader.iterShapeRecords(), title, unit=' records', total=len(sfreader))
@shp_util.command()
@click.argument('shp_file', type=click.Path(dir_okay=False))
@click.argument('field_name', type=str)
def list_field_values(shp_file, field_name):
"""
List the unique values within a field.
The field identified by FIELD_NAME
"""
with shapefile.Reader(shp_file) as sf:
field_values = set()
for record in _iterRecords(sf):
fval = record.as_dict().get(field_name)
if fval is not None:
field_values.add(fval)
print('Field values:')
for idx, fval in enumerate(field_values):
print(' {:02d}: {:s}'.format(idx+1, fval))
@shp_util.command()
@click.argument('src_shp_file', type=click.Path(dir_okay=False))
@click.argument('dst_shp_file', type=click.Path(dir_okay=False))
@click.argument('field_name')
@click.argument('replacement_pairs', nargs=-1)
def replace_field_values(src_shp_file, dst_shp_file, field_name, replacement_pairs):
"""
Search and replace of string values within a field.
The field is identified by FIELD_NAME. REPLACEMENT_PAIRS is expected to be in
the form of:
OLD1, NEW1, OLD2, NEW2, ..., OLDn, NEWn
where every occurance of OLD1 is replaced with NEW1 and so forth. This allows
for many search and replace operations to take place at once avoiding the need
for temporary intermediate files.
"""
# ordering matters. Opening dst first means closing it last
# allowing us to use the same file for destination and source
with shapefile.Writer(dst_shp_file) as dst:
with shapefile.Reader(src_shp_file) as src:
dst.shapeType = src.shapeType
# [1:] to skip the deletion field
dst.fields = src.fields[1:]
replacement_tab = dict()
for old_val, new_val in zip(replacement_pairs[0::2], replacement_pairs[1::2]):
replacement_tab[old_val] = new_val
for shaperec in _iterShapeRecords(src, 'Replacing field values in records'):
fval = shaperec.record.as_dict().get(field_name)
replacement = replacement_tab.get(fval)
if replacement is not None:
shaperec.record[field_name] = replacement
dst.shape(shaperec.shape)
dst.record(*shaperec.record)
@shp_util.command()
@click.argument('shp_file', type=click.Path(dir_okay=False))
def list_fields(shp_file):
"""
List fields within a shape file.
"""
with shapefile.Reader(shp_file) as sf:
for idx, fld in enumerate(sf.fields):
if idx == 0:
# ignore the deletion field
continue
fname, ftype, flen, fdplaces = fld
ftype_desc_tab = {
'C': 'STRING',
'N': 'NUMBER',
'F': 'NUMBER',
'L': 'BOOL',
'D': 'DATE',
'M': 'MEMO'
}
print('{:03d}: {:16s}\t{:7s}\t{:4d}'.format(idx+1,
fname,
ftype_desc_tab[ftype],
flen))
@shp_util.command()
@click.argument('shp_file', type=click.Path(dir_okay=False))
def info(shp_file):
"""
Prints metadata about the shp_file.
"""
with shapefile.Reader(shp_file) as sf:
print('Shape type:', sf.shapeTypeName)
print('Number of records:', len(sf))
if __name__ == '__main__':
import sys
sys.exit(shp_util())