-
Notifications
You must be signed in to change notification settings - Fork 22
/
isisnet2socet
278 lines (215 loc) · 9.08 KB
/
isisnet2socet
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#!/usr/bin/env python
import os
import math
import argparse
import warnings
import pvl
import math
import pyproj
import numpy as np
import pandas as pd
from plio.io.io_bae import save_gpf, save_ipf
import plio.io.io_controlnetwork as cn
import plio.io.isis_serial_number as sn
from plio.utils.utils import find_in_dict, split_all_ext
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('cnet_file', help='Path to an isis control network.')
parser.add_argument('e_radius', type=float, help='The semimajor radius of a given target.')
parser.add_argument('eccentricity', type=float, help='The eccentricity of a given target.')
parser.add_argument('cub_list', help='Path to a list file of all cubes being used. This \
includes file paths an extnsions.')
parser.add_argument('out_gpf', help='Path to save location of gpf file and new ipf files.')
parser.add_argument('--adjusted', help='Flag for saving apriori values or adjusted values',
default=False, required = False)
return parser.parse_args()
def reverse_known(record):
"""
Converts the known field from an isis dataframe into the
socet known column
Parameters
----------
record : object
Pandas series object
Returns
-------
: str
String representation of a known field
"""
lookup = {0:0,
2:0,
1:3,
3:3,
4:3}
record_type = record['known']
return lookup[record_type]
def reproject(record, semi_major, semi_minor, source_proj, dest_proj, **kwargs):
"""
Thin wrapper around PyProj's Transform() function to transform 1 or more three-dimensional
point from one coordinate system to another. If converting between Cartesian
body-centered body-fixed (BCBF) coordinates and Longitude/Latitude/Altitude coordinates,
the values input for semi-major and semi-minor axes determine whether latitudes are
planetographic or planetocentric and determine the shape of the datum for altitudes.
If semi_major == semi_minor, then latitudes are interpreted/created as planetocentric
and altitudes are interpreted/created as referenced to a spherical datum.
If semi_major != semi_minor, then latitudes are interpreted/created as planetographic
and altitudes are interpreted/created as referenced to an ellipsoidal datum.
Parameters
----------
record : object
Pandas series object
semi_major : float
Radius from the center of the body to the equater
semi_minor : float
Radius from the pole to the center of mass
source_proj : str
Pyproj string that defines a projection space ie. 'geocent'
dest_proj : str
Pyproj string that defines a project space ie. 'latlon'
Returns
-------
: list
Transformed coordinates as y, x, z
"""
source_pyproj = pyproj.Proj(proj = source_proj, a = semi_major, b = semi_minor)
dest_pyproj = pyproj.Proj(proj = dest_proj, a = semi_major, b = semi_minor)
y, x, z = pyproj.transform(source_pyproj, dest_pyproj, record[0], record[1], record[2], **kwargs)
return y, x, z
def fix_sample_line(record, serial_dict, cub_dict):
"""
Extracts the sample, line data from a cube and computes deviation from the
center of the image
Parameters
----------
record : dict
Dict containing the key serialnumber, l., and s.
serial_dict : dict
Maps serial numbers to images
cub_dict : dict
Maps basic cub names to their assocated absoluate path cubs
Returns
-------
new_line : int
new line deviation from the center
new_sample : int
new sample deviation from the center
"""
# Cube location to load
cube = pvl.load(cub_dict[serial_dict[record['serialnumber']]])
line_size = find_in_dict(cube, 'Lines')
sample_size = find_in_dict(cube, 'Samples')
new_line = record['l.'] - (int(line_size / 2.0)) - 1
new_sample = record['s.'] - (int(sample_size / 2.0)) - 1
return new_line, new_sample
def ignore_toggle(record):
"""
Maps the stat column in a record to 0 or 1 based on True or False
Parameters
----------
record : dict
Dict containing the key stat
"""
if record['stat'] == True:
return 0
else:
return 1
def apply_isis_transformations(df, eRadius, pRadius, serial_dict, cub_dict):
"""
Takes an ISIS3 control network dataframe and applies the necessary
transformations to convert that dataframe into a Socet Set-compatible
dataframe
Parameters
----------
df : object
Pandas dataframe object
eRadius : float
Equitorial radius of the target body
pRadius : float
Polar radius of the target body
serial_dict : dict
Dictionary mapping serials as keys to images as the values
cub_dict : str
Dictionary mapping the basename of IPF files as keys to image cube names as values
"""
# Convert from geocentered coords (x, y, z), to lat lon coords (latitude, longitude, alltitude)
ecef = np.array([[df['long_X_East']], [df['lat_Y_North']], [df['ht']]])
lla = reproject(ecef, semi_major = eRadius, semi_minor = pRadius,
source_proj = 'geocent', dest_proj = 'latlong')
df['long_X_East'], df['lat_Y_North'], df['ht'] = lla[0][0], lla[1][0], lla[2][0]
# Convert longitude and latitude from degrees to radians
df['long_X_East'] = df['long_X_East'].apply(np.radians)
df['lat_Y_North'] = df['lat_Y_North'].apply(np.radians)
# Update the stat fields and add the val field as it is just a clone of stat
df['stat'] = df.apply(ignore_toggle, axis = 1)
df['val'] = df['stat']
# Update the known field, add the ipf_file field for saving, and
# update the line, sample using data from the cubes
df['known'] = df.apply(reverse_known, axis = 1)
df['ipf_file'] = df['serialnumber'].apply(lambda serial_number: serial_dict[serial_number])
df['l.'], df['s.'] = zip(*df.apply(fix_sample_line, serial_dict = serial_dict,
cub_dict = cub_dict, axis = 1))
# Add dummy for generic value setting
x_dummy = lambda x: np.full(len(df), x)
df['sig0'] = x_dummy(1)
df['sig1'] = x_dummy(1)
df['sig2'] = x_dummy(1)
df['res0'] = x_dummy(0)
df['res1'] = x_dummy(0)
df['res2'] = x_dummy(0)
df['fid_x'] = x_dummy(0)
df['fid_y'] = x_dummy(0)
df['no_obs'] = x_dummy(1)
df['fid_val'] = x_dummy(0)
def main(args):
# Create cub dict to map ipf to cub
df = cn.from_isis(args.cnet_file)
e_radius = args.e_radius
p_radius = e_radius * math.sqrt((1 - (args.eccentricity ** 2)))
with open(args.cub_list, 'r') as f:
lines = f.readlines()
cub_list = [cub.replace('\n', '') for cub in lines]
out_gpf = args.out_gpf
adjusted_flag = args.adjusted
# Create cub dict to map ipf to cub
cub_dict = {split_all_ext(os.path.split(i)[1]): i for i in cub_list}
# Create serial dict to match serial to ipf
serial_dict = {sn.generate_serial_number(i): split_all_ext(os.path.split(i)[-1]) for i in cub_list}
# Remove duplicate columns
# There are better ways to do this but pandas was not having it
columns = []
column_index = []
for i, column in enumerate(list(df.columns)):
if column not in columns:
column_index.append(i)
columns.append(column)
df = df.iloc[:, column_index]
# Begin translation
# Remap the ISIS columns to socet column names
column_map = {'id': 'pt_id', 'line': 'l.', 'sample': 's.',
'lineResidual': 'res_l', 'sampleResidual': 'res_s', 'type': 'known',
'aprioriLatitudeSigma': 'sig0', 'aprioriLongitudeSigma': 'sig1', 'aprioriRadiusSigma': 'sig2',
'linesigma': 'sig_l', 'samplesigma': 'sig_s', 'ignore': 'stat'}
# Depending on the adjusted flag, set the renames for columns appropriately
if adjusted_flag:
column_map['adjustedY'] = 'lat_Y_North'
column_map['adjustedX'] = 'long_X_East'
column_map['adjustedZ'] = 'ht'
else:
column_map['aprioriY'] = 'lat_Y_North'
column_map['aprioriX'] = 'long_X_East'
column_map['aprioriZ'] = 'ht'
df.rename(columns = column_map, inplace=True)
apply_isis_transformations(df, e_radius, p_radius, serial_dict, cub_dict)
# Save the ipf(s)
save_ipf(df, os.path.split(out_gpf)[0])
# Get the first record from each group as there all the same, put them
# into a list, and sort it
points = [int(i[1].index[0]) for i in df.groupby('pt_id')]
points.sort()
# Set the gpf_df to only the values we need and do a small rename
gpf_df = df.iloc[points].copy()
gpf_df.rename(columns = {'pt_id': 'point_id'}, inplace=True)
# Save the gpf
save_gpf(gpf_df, out_gpf)
if __name__ == '__main__':
main(parse_args())