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

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

# http://pyrocko.org - GPLv3 

# 

# The Pyrocko Developers, 21st Century 

# ---|P------/S----------~Lg---------- 

from __future__ import absolute_import 

 

import re 

import logging 

import numpy as num 

from os import path as op 

from collections import OrderedDict 

import json 

 

from pyrocko import config, util 

 

 

def parse_3tup(s): 

m = re.match(r'^\(?([^,]+),([^,]*),([^,]*)\)$', s) 

if m: 

return [float(m.group(1)) if m.group(1) else None for i in range(3)] 

else: 

return [None, None, None] 

 

 

logger = logging.getLogger('ActiveFaults') 

 

 

class Fault(object): 

__fields__ = OrderedDict() 

__slots__ = list(__fields__.keys()) 

 

def get_property(self, fault_obj, attr): 

try: 

values = float(fault_obj['properties'][attr][1:3]) 

except KeyError: 

if attr == 'lower_seis_depth' or attr == 'upper_seis_depth': 

values = 0 

else: 

values = -999 

return values 

 

def __init__(self, f): 

nodes = f['geometry']['coordinates'] 

props = f['properties'] 

lons = [p[0] for p in nodes] 

lats = [p[1] for p in nodes] 

self.lon = lons 

self.lat = lats 

self.slip_type = props.get('slip_type', 'Unknown') 

 

for attr, attr_type in self.__fields__.items(): 

if attr in props: 

if not props[attr]: 

continue 

 

if isinstance(props[attr], attr_type): 

v = props[attr] 

 

elif attr_type is float: 

if re.match(r'^\(?(([ \-0-9,.]|nan)*)\)$', props[attr]): 

v = parse_3tup(props[attr])[0] 

else: 

v = float(props[attr]) 

 

elif attr_type is int: 

v = int(props[attr]) 

 

else: 

v = None 

 

setattr(self, attr, v) 

 

def get_surface_line(self): 

arr = num.empty((len(self.lat), 2)) 

for i in range(len(self.lat)): 

arr[i, 0] = self.lat[i] 

arr[i, 1] = self.lon[i] 

 

return arr 

 

def __str__(self): 

d = {attr: getattr(self, attr) for attr in self.__fields__.keys()} 

return '\n'.join(['%s: %s' % (attr, val) for attr, val in d.items()]) 

 

 

class ActiveFault(Fault): 

__fields__ = OrderedDict([ 

 

('lat', list), 

('lon', list), 

('average_dip', float), 

('average_rake', float), 

('lower_seis_depth', float), 

('upper_seis_depth', float), 

('slip_type', str), 

]) 

 

 

class ActiveFaults(object): 

URL_GEM_ACTIVE_FAULTS = 'https://raw.githubusercontent.com/cossatot/gem-global-active-faults/master/geojson/gem_active_faults.geojson' # noqa 

 

def __init__(self): 

self.fname_active_faults = op.join( 

config.config().fault_lines_dir, 'gem_active_faults.geojson') 

 

if not op.exists(self.fname_active_faults): 

self.download() 

 

self.active_faults = [] 

self._load_faults(self.fname_active_faults, ActiveFault) 

 

def _load_faults(self, fname, cls): 

with open(fname, 'r') as f: 

gj = json.load(f) 

faults = gj['features'] 

for f in faults: 

fault = cls(f) 

self.active_faults.append(fault) 

logger.debug('loaded %d fault', self.nactive_faults) 

 

def download(self): 

logger.info('Downloading GEM active faults database...') 

util.download_file(self.URL_GEM_ACTIVE_FAULTS, 

self.fname_active_faults) 

 

@property 

def nactive_faults(self): 

return len(self.active_faults) 

 

def nactive_faults_nodes(self): 

return int(sum(len(f.lat) for f in self.active_faults)) 

 

def get_coords(self): 

return num.array([f['coordinates'] for f in self.active_faults]) 

 

 

if __name__ == '__main__': 

logging.basicConfig(level=logging.DEBUG) 

activefaults = ActiveFaults()