Coverage for gws-app/gws/lib/misc/tree_viewer/__init__.py: 0%
112 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""Take a pickled GWS tree and output a browsable HTML page."""
3import builtins
4import json
5import os
6import pickle
7import sys
8import re
11def load_pickle(path):
12 class Unpickler(pickle.Unpickler):
13 def find_class(self, module, name):
14 if module == 'builtins':
15 return getattr(builtins, name)
17 class T:
18 KLASS = module + '.' + name
20 def __init__(self, *args):
21 pass
23 def __setstate__(self, state):
24 if not isinstance(state, dict):
25 state = {'?': state}
26 self.STATE = state
28 def __getattr__(self, item):
29 return '?' + item
31 def __call__(self, *args, **kwargs):
32 pass
34 def __setitem__(self, *args, **kwargs):
35 pass
37 def _unpickle(self, *args):
38 pass
40 return T
42 hash_map = {}
43 obj_list = []
45 def transform(obj):
46 if obj is None:
47 return obj
48 if isinstance(obj, (str, int, float, bool)):
49 return obj
50 if isinstance(obj, (list, tuple, set)):
51 return [transform(x) for x in obj]
52 if isinstance(obj, dict):
53 return {str(k): transform(v) for k, v in obj.items()}
54 if hasattr(obj, 'KLASS'):
55 h = hash(obj)
56 if h in hash_map:
57 return hash_map[h]
59 idx = len(obj_list)
60 obj_list.append('dummy value')
62 cls = obj.KLASS
63 if isinstance(obj, type):
64 cls = 'CLASS:' + cls
66 state = getattr(obj, 'STATE', {})
67 if not isinstance(state, dict):
68 state = {'state': repr(state)}
70 name = '$.' + cls + ':' + str(idx)
71 if state.get('uid'):
72 name += ':uid=' + str(state['uid'])
73 hash_map[h] = name
75 d = {str(k): transform(v) for k, v in state.items()}
76 d['$'] = name
77 obj_list[idx] = d
79 return name
81 return 'UNKNOWN: ' + repr(obj)
83 def flat(val):
84 if isinstance(val, str):
85 return [val]
86 if isinstance(val, (list, tuple, set)):
87 return [v for e in val for v in flat(e)]
88 if isinstance(val, dict):
89 return [v for e in val.values() for v in flat(e)]
90 return []
92 def make_refs():
93 ref_map = {d['$']: d for d in obj_list}
94 for d in obj_list:
95 for prop, v in d.items():
96 if prop == '$':
97 continue
98 for v2 in flat(v):
99 if v2 in ref_map and v2 != d['$']:
100 ref_map[v2].setdefault('$REFS', []).append(d['$'] + ' @ ' + prop)
102 with open(path, 'rb') as fp:
103 tree = Unpickler(fp).load()
105 transform(tree)
106 make_refs()
108 return obj_list
111path = sys.argv[1]
112obj_list = load_pickle(path)
114try:
115 mode = sys.argv[2]
116except:
117 mode = 'json'
119path = json.dumps(path)
120data = json.dumps(obj_list, indent=4, sort_keys=True).replace('</script>', '<\\/script>')
122if mode == 'html':
123 cdir = os.path.dirname(__file__)
125 with open(cdir + '/css.css') as fp:
126 css = f'<style>\n{fp.read()}\n</style>'
127 with open(cdir + '/js.js') as fp:
128 js = f'<script>\n{fp.read()}\n</script>'
130 # for debugging
131 # css = f'<link rel="stylesheet" href="{cdir}/css.css">'
132 # js = f'<script src="{cdir}/js.js"></script>'
134 content = f"""<!doctype html>
135 <html>
136 <head>
137 <meta charset="utf-8">
138 {css}
139 </head>
140 <body>
141 <script>PATH={path}</script>
142 <script>DATA={data}</script>
143 {js}
144 </body>
145 </html>
146 """
148 print(content)
150if mode == 'json':
151 print(data)
153if mode == 'bounds':
154 obj_map = {d['$']: d for d in obj_list}
156 for d in obj_list:
157 b = d.get('wgsExtent')
158 if not b:
159 continue
160 x1, y1, x2, y2 = b
161 feature = {
162 "type": "Feature",
163 "properties": {
164 "id": d['$'],
165 "name": d.get('name') or d.get('title') or '',
166 },
167 "geometry": {
168 "type": "Polygon",
169 "coordinates": [
170 [[x1, y1], [x1, y2], [x2, y2], [x2, y1], [x1, y1]]
171 ]
172 }
173 }
174 file_name = re.sub(
175 r'\W+',
176 '_',
177 feature['properties']['name'] + '_' + feature['properties']['id'],
178 )
179 fc = {
180 "type": "FeatureCollection",
181 "features": [feature],
182 }
183 with open(f'{file_name}.geojson', 'w') as fp:
184 json.dump(fc, fp, indent=4)